Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test suite for SMB2 ioctl operations
5 :
6 : Copyright (C) David Disseldorp 2011-2016
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "librpc/gen_ndr/security.h"
24 : #include "libcli/smb2/smb2.h"
25 : #include "libcli/smb2/smb2_calls.h"
26 : #include "torture/torture.h"
27 : #include "torture/smb2/proto.h"
28 : #include "../libcli/smb/smbXcli_base.h"
29 : #include "librpc/gen_ndr/ndr_ioctl.h"
30 :
31 : #define FNAME "testfsctl.dat"
32 : #define FNAME2 "testfsctl2.dat"
33 : #define DNAME "testfsctl_dir"
34 :
35 : /*
36 : basic testing of SMB2 shadow copy calls
37 : */
38 7 : static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39 : struct smb2_tree *tree)
40 : {
41 : struct smb2_handle h;
42 : uint8_t buf[100];
43 : NTSTATUS status;
44 : union smb_ioctl ioctl;
45 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
46 :
47 7 : smb2_util_unlink(tree, FNAME);
48 :
49 7 : status = torture_smb2_testfile(tree, FNAME, &h);
50 7 : torture_assert_ntstatus_ok(torture, status, "create write");
51 :
52 7 : ZERO_ARRAY(buf);
53 7 : status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54 7 : torture_assert_ntstatus_ok(torture, status, "write");
55 :
56 7 : ZERO_STRUCT(ioctl);
57 7 : ioctl.smb2.level = RAW_IOCTL_SMB2;
58 7 : ioctl.smb2.in.file.handle = h;
59 7 : ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60 7 : ioctl.smb2.in.max_output_response = 16;
61 7 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
62 :
63 7 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64 7 : if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65 7 : || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66 7 : torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
67 : }
68 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
69 :
70 0 : return true;
71 : }
72 :
73 : /*
74 : basic testing of the SMB2 server side copy ioctls
75 : */
76 7 : static bool test_ioctl_req_resume_key(struct torture_context *torture,
77 : struct smb2_tree *tree)
78 : {
79 : struct smb2_handle h;
80 : uint8_t buf[100];
81 : NTSTATUS status;
82 : union smb_ioctl ioctl;
83 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
84 : struct req_resume_key_rsp res_key;
85 : enum ndr_err_code ndr_ret;
86 :
87 7 : smb2_util_unlink(tree, FNAME);
88 :
89 7 : status = torture_smb2_testfile(tree, FNAME, &h);
90 7 : torture_assert_ntstatus_ok(torture, status, "create write");
91 :
92 7 : ZERO_ARRAY(buf);
93 7 : status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94 7 : torture_assert_ntstatus_ok(torture, status, "write");
95 :
96 7 : ZERO_STRUCT(ioctl);
97 7 : ioctl.smb2.level = RAW_IOCTL_SMB2;
98 7 : ioctl.smb2.in.file.handle = h;
99 7 : ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100 7 : ioctl.smb2.in.max_output_response = 32;
101 7 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
102 :
103 7 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104 7 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
105 :
106 6 : 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 6 : torture_assert_ndr_success(torture, ndr_ret,
109 : "ndr_pull_req_resume_key_rsp");
110 :
111 6 : ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
112 :
113 6 : talloc_free(tmp_ctx);
114 6 : return true;
115 : }
116 :
117 : /*
118 : testing fetching a resume key twice for one file handle
119 : */
120 7 : static bool test_ioctl_req_two_resume_keys(struct torture_context *torture,
121 : struct smb2_tree *tree)
122 : {
123 : struct smb2_handle h;
124 : uint8_t buf[100];
125 : NTSTATUS status;
126 : union smb_ioctl ioctl;
127 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
128 : struct req_resume_key_rsp res_key;
129 : enum ndr_err_code ndr_ret;
130 :
131 7 : smb2_util_unlink(tree, FNAME);
132 :
133 7 : status = torture_smb2_testfile(tree, FNAME, &h);
134 7 : torture_assert_ntstatus_ok(torture, status, "create write");
135 :
136 7 : ZERO_ARRAY(buf);
137 7 : status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
138 7 : torture_assert_ntstatus_ok(torture, status, "write");
139 :
140 7 : ZERO_STRUCT(ioctl);
141 7 : ioctl.smb2.level = RAW_IOCTL_SMB2;
142 7 : ioctl.smb2.in.file.handle = h;
143 7 : ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
144 7 : ioctl.smb2.in.max_output_response = 32;
145 7 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
146 :
147 7 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
148 7 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
149 :
150 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
151 : (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
152 6 : torture_assert_ndr_success(torture, ndr_ret,
153 : "ndr_pull_req_resume_key_rsp");
154 :
155 6 : ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
156 :
157 6 : ZERO_STRUCT(ioctl);
158 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
159 6 : ioctl.smb2.in.file.handle = h;
160 6 : ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
161 6 : ioctl.smb2.in.max_output_response = 32;
162 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
163 :
164 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
165 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
166 :
167 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
168 : (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
169 6 : torture_assert_ndr_success(torture, ndr_ret,
170 : "ndr_pull_req_resume_key_rsp");
171 :
172 6 : ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
173 :
174 6 : talloc_free(tmp_ctx);
175 6 : return true;
176 : }
177 :
178 29194402 : static uint64_t patt_hash(uint64_t off)
179 : {
180 29194402 : return off;
181 : }
182 :
183 427 : static bool write_pattern(struct torture_context *torture,
184 : struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
185 : struct smb2_handle h, uint64_t off, uint64_t len,
186 : uint64_t patt_off)
187 : {
188 : NTSTATUS status;
189 : uint64_t i;
190 : uint8_t *buf;
191 427 : uint64_t io_sz = MIN(1024 * 64, len);
192 :
193 427 : if (len == 0) {
194 0 : return true;
195 : }
196 :
197 427 : torture_assert(torture, (len % 8) == 0, "invalid write len");
198 :
199 427 : buf = talloc_zero_size(mem_ctx, io_sz);
200 427 : torture_assert(torture, (buf != NULL), "no memory for file data buf");
201 :
202 1478 : while (len > 0) {
203 3349860 : for (i = 0; i <= io_sz - 8; i += 8) {
204 3349172 : SBVAL(buf, i, patt_hash(patt_off));
205 3349172 : patt_off += 8;
206 : }
207 :
208 688 : status = smb2_util_write(tree, h,
209 : buf, off, io_sz);
210 688 : torture_assert_ntstatus_ok(torture, status, "file write");
211 :
212 688 : len -= io_sz;
213 688 : off += io_sz;
214 : }
215 :
216 427 : talloc_free(buf);
217 :
218 427 : return true;
219 : }
220 :
221 144 : static bool check_pattern(struct torture_context *torture,
222 : struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
223 : struct smb2_handle h, uint64_t off, uint64_t len,
224 : uint64_t patt_off)
225 : {
226 144 : if (len == 0) {
227 0 : return true;
228 : }
229 :
230 144 : torture_assert(torture, (len % 8) == 0, "invalid read len");
231 :
232 678 : while (len > 0) {
233 : uint64_t i;
234 : struct smb2_read r;
235 : NTSTATUS status;
236 414 : uint64_t io_sz = MIN(1024 * 64, len);
237 :
238 414 : ZERO_STRUCT(r);
239 414 : r.in.file.handle = h;
240 414 : r.in.length = io_sz;
241 414 : r.in.offset = off;
242 414 : status = smb2_read(tree, mem_ctx, &r);
243 414 : torture_assert_ntstatus_ok(torture, status, "read");
244 :
245 414 : torture_assert_u64_equal(torture, r.out.data.length, io_sz,
246 : "read data len mismatch");
247 :
248 2401440 : for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
249 2401026 : uint64_t data = BVAL(r.out.data.data, i);
250 2401026 : torture_assert_u64_equal(torture, data, patt_hash(patt_off),
251 : talloc_asprintf(torture, "read data "
252 : "pattern bad at %llu\n",
253 : (unsigned long long)off + i));
254 : }
255 414 : talloc_free(r.out.data.data);
256 414 : len -= io_sz;
257 414 : off += io_sz;
258 : }
259 :
260 144 : return true;
261 : }
262 :
263 60 : static bool check_zero(struct torture_context *torture,
264 : struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
265 : struct smb2_handle h, uint64_t off, uint64_t len)
266 : {
267 : uint64_t i;
268 : struct smb2_read r;
269 : NTSTATUS status;
270 :
271 60 : if (len == 0) {
272 6 : return true;
273 : }
274 :
275 54 : ZERO_STRUCT(r);
276 54 : r.in.file.handle = h;
277 54 : r.in.length = len;
278 54 : r.in.offset = off;
279 54 : status = smb2_read(tree, mem_ctx, &r);
280 54 : torture_assert_ntstatus_ok(torture, status, "read");
281 :
282 54 : torture_assert_u64_equal(torture, r.out.data.length, len,
283 : "read data len mismatch");
284 :
285 166710 : for (i = 0; i <= len - 8; i += 8) {
286 166656 : uint64_t data = BVAL(r.out.data.data, i);
287 166656 : torture_assert_u64_equal(torture, data, 0,
288 : talloc_asprintf(mem_ctx, "read zero "
289 : "bad at %llu\n",
290 : (unsigned long long)i));
291 : }
292 :
293 54 : talloc_free(r.out.data.data);
294 54 : return true;
295 : }
296 :
297 919 : static bool test_setup_open(struct torture_context *torture,
298 : struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
299 : const char *fname,
300 : struct smb2_handle *fh,
301 : uint32_t desired_access,
302 : uint32_t file_attributes)
303 : {
304 : struct smb2_create io;
305 : NTSTATUS status;
306 :
307 919 : ZERO_STRUCT(io);
308 919 : io.in.desired_access = desired_access;
309 919 : io.in.file_attributes = file_attributes;
310 919 : io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
311 919 : io.in.share_access =
312 : NTCREATEX_SHARE_ACCESS_DELETE|
313 : NTCREATEX_SHARE_ACCESS_READ|
314 : NTCREATEX_SHARE_ACCESS_WRITE;
315 919 : if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
316 21 : io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
317 : }
318 919 : io.in.fname = fname;
319 :
320 919 : status = smb2_create(tree, mem_ctx, &io);
321 919 : torture_assert_ntstatus_ok(torture, status, "file create");
322 :
323 919 : *fh = io.out.file.handle;
324 :
325 919 : return true;
326 : }
327 :
328 815 : static bool test_setup_create_fill(struct torture_context *torture,
329 : struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
330 : const char *fname,
331 : struct smb2_handle *fh,
332 : uint64_t size,
333 : uint32_t desired_access,
334 : uint32_t file_attributes)
335 : {
336 : bool ok;
337 815 : uint32_t initial_access = desired_access;
338 :
339 815 : if (size > 0) {
340 331 : initial_access |= SEC_FILE_APPEND_DATA;
341 : }
342 :
343 815 : smb2_util_unlink(tree, fname);
344 :
345 815 : ok = test_setup_open(torture, tree, mem_ctx,
346 : fname,
347 : fh,
348 : initial_access,
349 : file_attributes);
350 815 : torture_assert(torture, ok, "file create");
351 :
352 815 : if (size > 0) {
353 331 : ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
354 331 : torture_assert(torture, ok, "write pattern");
355 : }
356 :
357 815 : if (initial_access != desired_access) {
358 38 : smb2_util_close(tree, *fh);
359 38 : ok = test_setup_open(torture, tree, mem_ctx,
360 : fname,
361 : fh,
362 : desired_access,
363 : file_attributes);
364 38 : torture_assert(torture, ok, "file open");
365 : }
366 :
367 815 : return true;
368 : }
369 :
370 184 : static bool test_setup_copy_chunk(struct torture_context *torture,
371 : struct smb2_tree *src_tree,
372 : struct smb2_tree *dst_tree,
373 : TALLOC_CTX *mem_ctx,
374 : uint32_t nchunks,
375 : const char *src_name,
376 : struct smb2_handle *src_h,
377 : uint64_t src_size,
378 : uint32_t src_desired_access,
379 : const char *dst_name,
380 : struct smb2_handle *dest_h,
381 : uint64_t dest_size,
382 : uint32_t dest_desired_access,
383 : struct srv_copychunk_copy *cc_copy,
384 : union smb_ioctl *ioctl)
385 : {
386 : struct req_resume_key_rsp res_key;
387 : bool ok;
388 : NTSTATUS status;
389 : enum ndr_err_code ndr_ret;
390 :
391 184 : ok = test_setup_create_fill(torture, src_tree, mem_ctx, src_name,
392 : src_h, src_size, src_desired_access,
393 : FILE_ATTRIBUTE_NORMAL);
394 184 : torture_assert(torture, ok, "src file create fill");
395 :
396 184 : ok = test_setup_create_fill(torture, dst_tree, mem_ctx, dst_name,
397 : dest_h, dest_size, dest_desired_access,
398 : FILE_ATTRIBUTE_NORMAL);
399 184 : torture_assert(torture, ok, "dest file create fill");
400 :
401 184 : ZERO_STRUCTPN(ioctl);
402 184 : ioctl->smb2.level = RAW_IOCTL_SMB2;
403 184 : ioctl->smb2.in.file.handle = *src_h;
404 184 : ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
405 : /* Allow for Key + ContextLength + Context */
406 184 : ioctl->smb2.in.max_output_response = 32;
407 184 : ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
408 :
409 184 : status = smb2_ioctl(src_tree, mem_ctx, &ioctl->smb2);
410 184 : torture_assert_ntstatus_ok(torture, status,
411 : "FSCTL_SRV_REQUEST_RESUME_KEY");
412 :
413 162 : ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
414 : (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
415 :
416 162 : torture_assert_ndr_success(torture, ndr_ret,
417 : "ndr_pull_req_resume_key_rsp");
418 :
419 162 : ZERO_STRUCTPN(ioctl);
420 162 : ioctl->smb2.level = RAW_IOCTL_SMB2;
421 162 : ioctl->smb2.in.file.handle = *dest_h;
422 162 : ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
423 162 : ioctl->smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp);
424 162 : ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
425 :
426 162 : ZERO_STRUCTPN(cc_copy);
427 162 : memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
428 162 : cc_copy->chunk_count = nchunks;
429 162 : cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
430 162 : torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
431 :
432 162 : return true;
433 : }
434 :
435 :
436 108 : static bool check_copy_chunk_rsp(struct torture_context *torture,
437 : struct srv_copychunk_rsp *cc_rsp,
438 : uint32_t ex_chunks_written,
439 : uint32_t ex_chunk_bytes_written,
440 : uint32_t ex_total_bytes_written)
441 : {
442 108 : torture_assert_int_equal(torture, cc_rsp->chunks_written,
443 : ex_chunks_written, "num chunks");
444 108 : torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
445 : ex_chunk_bytes_written, "chunk bytes written");
446 108 : torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
447 : ex_total_bytes_written, "chunk total bytes");
448 108 : return true;
449 : }
450 :
451 7 : static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
452 : struct smb2_tree *tree)
453 : {
454 : struct smb2_handle src_h;
455 : struct smb2_handle dest_h;
456 : NTSTATUS status;
457 : union smb_ioctl ioctl;
458 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
459 : struct srv_copychunk_copy cc_copy;
460 : struct srv_copychunk_rsp cc_rsp;
461 : enum ndr_err_code ndr_ret;
462 : bool ok;
463 :
464 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
465 : 1, /* 1 chunk */
466 : FNAME,
467 : &src_h, 4096, /* fill 4096 byte src file */
468 : SEC_RIGHTS_FILE_ALL,
469 : FNAME2,
470 : &dest_h, 0, /* 0 byte dest file */
471 : SEC_RIGHTS_FILE_ALL,
472 : &cc_copy,
473 : &ioctl);
474 7 : if (!ok) {
475 1 : torture_fail(torture, "setup copy chunk error");
476 : }
477 :
478 : /* copy all src file data (via a single chunk desc) */
479 6 : cc_copy.chunks[0].source_off = 0;
480 6 : cc_copy.chunks[0].target_off = 0;
481 6 : cc_copy.chunks[0].length = 4096;
482 :
483 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
484 : &cc_copy,
485 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
486 6 : torture_assert_ndr_success(torture, ndr_ret,
487 : "ndr_push_srv_copychunk_copy");
488 :
489 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
490 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
491 :
492 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
493 : &cc_rsp,
494 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
495 6 : torture_assert_ndr_success(torture, ndr_ret,
496 : "ndr_pull_srv_copychunk_rsp");
497 :
498 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
499 : 1, /* chunks written */
500 : 0, /* chunk bytes unsuccessfully written */
501 : 4096); /* total bytes written */
502 6 : if (!ok) {
503 0 : torture_fail(torture, "bad copy chunk response data");
504 : }
505 :
506 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
507 6 : if (!ok) {
508 0 : torture_fail(torture, "inconsistent file data");
509 : }
510 :
511 6 : smb2_util_close(tree, src_h);
512 6 : smb2_util_close(tree, dest_h);
513 6 : talloc_free(tmp_ctx);
514 6 : return true;
515 : }
516 :
517 7 : static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
518 : struct smb2_tree *tree)
519 : {
520 : struct smb2_handle src_h;
521 : struct smb2_handle dest_h;
522 : NTSTATUS status;
523 : union smb_ioctl ioctl;
524 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
525 : struct srv_copychunk_copy cc_copy;
526 : struct srv_copychunk_rsp cc_rsp;
527 : enum ndr_err_code ndr_ret;
528 : bool ok;
529 :
530 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
531 : 2, /* chunks */
532 : FNAME,
533 : &src_h, 8192, /* src file */
534 : SEC_RIGHTS_FILE_ALL,
535 : FNAME2,
536 : &dest_h, 0, /* dest file */
537 : SEC_RIGHTS_FILE_ALL,
538 : &cc_copy,
539 : &ioctl);
540 7 : if (!ok) {
541 1 : torture_fail(torture, "setup copy chunk error");
542 : }
543 :
544 : /* copy all src file data via two chunks */
545 6 : cc_copy.chunks[0].source_off = 0;
546 6 : cc_copy.chunks[0].target_off = 0;
547 6 : cc_copy.chunks[0].length = 4096;
548 :
549 6 : cc_copy.chunks[1].source_off = 4096;
550 6 : cc_copy.chunks[1].target_off = 4096;
551 6 : cc_copy.chunks[1].length = 4096;
552 :
553 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
554 : &cc_copy,
555 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
556 6 : torture_assert_ndr_success(torture, ndr_ret,
557 : "ndr_push_srv_copychunk_copy");
558 :
559 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
560 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
561 :
562 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
563 : &cc_rsp,
564 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
565 6 : torture_assert_ndr_success(torture, ndr_ret,
566 : "ndr_pull_srv_copychunk_rsp");
567 :
568 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
569 : 2, /* chunks written */
570 : 0, /* chunk bytes unsuccessfully written */
571 : 8192); /* total bytes written */
572 6 : if (!ok) {
573 0 : torture_fail(torture, "bad copy chunk response data");
574 : }
575 :
576 6 : smb2_util_close(tree, src_h);
577 6 : smb2_util_close(tree, dest_h);
578 6 : talloc_free(tmp_ctx);
579 6 : return true;
580 : }
581 :
582 7 : static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
583 : struct smb2_tree *tree)
584 : {
585 : struct smb2_handle src_h;
586 : struct smb2_handle dest_h;
587 : NTSTATUS status;
588 : union smb_ioctl ioctl;
589 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
590 : struct srv_copychunk_copy cc_copy;
591 : struct srv_copychunk_rsp cc_rsp;
592 : enum ndr_err_code ndr_ret;
593 : bool ok;
594 :
595 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
596 : 2, /* chunks */
597 : FNAME,
598 : &src_h, 96, /* src file */
599 : SEC_RIGHTS_FILE_ALL,
600 : FNAME2,
601 : &dest_h, 0, /* dest file */
602 : SEC_RIGHTS_FILE_ALL,
603 : &cc_copy,
604 : &ioctl);
605 7 : if (!ok) {
606 1 : torture_fail(torture, "setup copy chunk error");
607 : }
608 :
609 : /* copy all src file data via two chunks, sub block size chunks */
610 6 : cc_copy.chunks[0].source_off = 0;
611 6 : cc_copy.chunks[0].target_off = 0;
612 6 : cc_copy.chunks[0].length = 48;
613 :
614 6 : cc_copy.chunks[1].source_off = 48;
615 6 : cc_copy.chunks[1].target_off = 48;
616 6 : cc_copy.chunks[1].length = 48;
617 :
618 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
619 : &cc_copy,
620 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
621 6 : torture_assert_ndr_success(torture, ndr_ret,
622 : "ndr_push_srv_copychunk_copy");
623 :
624 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
625 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
626 :
627 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
628 : &cc_rsp,
629 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
630 6 : torture_assert_ndr_success(torture, ndr_ret,
631 : "ndr_pull_srv_copychunk_rsp");
632 :
633 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
634 : 2, /* chunks written */
635 : 0, /* chunk bytes unsuccessfully written */
636 : 96); /* total bytes written */
637 6 : if (!ok) {
638 0 : torture_fail(torture, "bad copy chunk response data");
639 : }
640 :
641 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
642 6 : if (!ok) {
643 0 : torture_fail(torture, "inconsistent file data");
644 : }
645 :
646 6 : smb2_util_close(tree, src_h);
647 6 : smb2_util_close(tree, dest_h);
648 6 : talloc_free(tmp_ctx);
649 6 : return true;
650 : }
651 :
652 7 : static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
653 : struct smb2_tree *tree)
654 : {
655 : struct smb2_handle src_h;
656 : struct smb2_handle dest_h;
657 : NTSTATUS status;
658 : union smb_ioctl ioctl;
659 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
660 : struct srv_copychunk_copy cc_copy;
661 : struct srv_copychunk_rsp cc_rsp;
662 : enum ndr_err_code ndr_ret;
663 : bool ok;
664 :
665 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
666 : 2, /* chunks */
667 : FNAME,
668 : &src_h, 8192, /* src file */
669 : SEC_RIGHTS_FILE_ALL,
670 : FNAME2,
671 : &dest_h, 4096, /* dest file */
672 : SEC_RIGHTS_FILE_ALL,
673 : &cc_copy,
674 : &ioctl);
675 7 : if (!ok) {
676 1 : torture_fail(torture, "setup copy chunk error");
677 : }
678 :
679 : /* first chunk overwrites existing dest data */
680 6 : cc_copy.chunks[0].source_off = 0;
681 6 : cc_copy.chunks[0].target_off = 0;
682 6 : cc_copy.chunks[0].length = 4096;
683 :
684 : /* second chunk overwrites the first */
685 6 : cc_copy.chunks[1].source_off = 4096;
686 6 : cc_copy.chunks[1].target_off = 0;
687 6 : cc_copy.chunks[1].length = 4096;
688 :
689 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
690 : &cc_copy,
691 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
692 6 : torture_assert_ndr_success(torture, ndr_ret,
693 : "ndr_push_srv_copychunk_copy");
694 :
695 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
696 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
697 :
698 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
699 : &cc_rsp,
700 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
701 6 : torture_assert_ndr_success(torture, ndr_ret,
702 : "ndr_pull_srv_copychunk_rsp");
703 :
704 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
705 : 2, /* chunks written */
706 : 0, /* chunk bytes unsuccessfully written */
707 : 8192); /* total bytes written */
708 6 : if (!ok) {
709 0 : torture_fail(torture, "bad copy chunk response data");
710 : }
711 :
712 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
713 6 : if (!ok) {
714 0 : torture_fail(torture, "inconsistent file data");
715 : }
716 :
717 6 : smb2_util_close(tree, src_h);
718 6 : smb2_util_close(tree, dest_h);
719 6 : talloc_free(tmp_ctx);
720 6 : return true;
721 : }
722 :
723 7 : static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
724 : struct smb2_tree *tree)
725 : {
726 : struct smb2_handle src_h;
727 : struct smb2_handle dest_h;
728 : NTSTATUS status;
729 : union smb_ioctl ioctl;
730 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
731 : struct srv_copychunk_copy cc_copy;
732 : struct srv_copychunk_rsp cc_rsp;
733 : enum ndr_err_code ndr_ret;
734 : bool ok;
735 :
736 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
737 : 2, /* chunks */
738 : FNAME,
739 : &src_h, 4096, /* src file */
740 : SEC_RIGHTS_FILE_ALL,
741 : FNAME2,
742 : &dest_h, 0, /* dest file */
743 : SEC_RIGHTS_FILE_ALL,
744 : &cc_copy,
745 : &ioctl);
746 7 : if (!ok) {
747 1 : torture_fail(torture, "setup copy chunk error");
748 : }
749 :
750 6 : cc_copy.chunks[0].source_off = 0;
751 6 : cc_copy.chunks[0].target_off = 0;
752 6 : cc_copy.chunks[0].length = 4096;
753 :
754 : /* second chunk appends the same data to the first */
755 6 : cc_copy.chunks[1].source_off = 0;
756 6 : cc_copy.chunks[1].target_off = 4096;
757 6 : cc_copy.chunks[1].length = 4096;
758 :
759 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
760 : &cc_copy,
761 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
762 6 : torture_assert_ndr_success(torture, ndr_ret,
763 : "ndr_push_srv_copychunk_copy");
764 :
765 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
766 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
767 :
768 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
769 : &cc_rsp,
770 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
771 6 : torture_assert_ndr_success(torture, ndr_ret,
772 : "ndr_pull_srv_copychunk_rsp");
773 :
774 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
775 : 2, /* chunks written */
776 : 0, /* chunk bytes unsuccessfully written */
777 : 8192); /* total bytes written */
778 6 : if (!ok) {
779 0 : torture_fail(torture, "bad copy chunk response data");
780 : }
781 :
782 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
783 6 : if (!ok) {
784 0 : torture_fail(torture, "inconsistent file data");
785 : }
786 :
787 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
788 6 : if (!ok) {
789 0 : torture_fail(torture, "inconsistent file data");
790 : }
791 :
792 6 : smb2_util_close(tree, src_h);
793 6 : smb2_util_close(tree, dest_h);
794 6 : talloc_free(tmp_ctx);
795 6 : return true;
796 : }
797 :
798 7 : static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
799 : struct smb2_tree *tree)
800 : {
801 : struct smb2_handle src_h;
802 : struct smb2_handle dest_h;
803 : NTSTATUS status;
804 : union smb_ioctl ioctl;
805 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
806 : struct srv_copychunk_copy cc_copy;
807 : struct srv_copychunk_rsp cc_rsp;
808 : enum ndr_err_code ndr_ret;
809 : bool ok;
810 :
811 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
812 : 1, /* chunks */
813 : FNAME,
814 : &src_h, 4096, /* src file */
815 : SEC_RIGHTS_FILE_ALL,
816 : FNAME2,
817 : &dest_h, 0, /* dest file */
818 : SEC_RIGHTS_FILE_ALL,
819 : &cc_copy,
820 : &ioctl);
821 7 : if (!ok) {
822 1 : torture_fail(torture, "setup copy chunk error");
823 : }
824 :
825 : /* send huge chunk length request */
826 6 : cc_copy.chunks[0].source_off = 0;
827 6 : cc_copy.chunks[0].target_off = 0;
828 6 : cc_copy.chunks[0].length = UINT_MAX;
829 :
830 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
831 : &cc_copy,
832 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
833 6 : torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
834 :
835 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
836 6 : torture_assert_ntstatus_equal(torture, status,
837 : NT_STATUS_INVALID_PARAMETER,
838 : "bad oversize chunk response");
839 :
840 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
841 : &cc_rsp,
842 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
843 6 : torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
844 :
845 6 : torture_comment(torture, "limit max chunks, got %u\n",
846 : cc_rsp.chunks_written);
847 6 : torture_comment(torture, "limit max chunk len, got %u\n",
848 : cc_rsp.chunk_bytes_written);
849 6 : torture_comment(torture, "limit max total bytes, got %u\n",
850 : cc_rsp.total_bytes_written);
851 :
852 6 : smb2_util_close(tree, src_h);
853 6 : smb2_util_close(tree, dest_h);
854 6 : talloc_free(tmp_ctx);
855 6 : return true;
856 : }
857 :
858 7 : static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
859 : struct smb2_tree *tree)
860 : {
861 : struct smb2_handle src_h;
862 : struct smb2_handle src_h2;
863 : struct smb2_handle dest_h;
864 : NTSTATUS status;
865 : union smb_ioctl ioctl;
866 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
867 : struct srv_copychunk_copy cc_copy;
868 : struct srv_copychunk_rsp cc_rsp;
869 : enum ndr_err_code ndr_ret;
870 : bool ok;
871 : struct smb2_lock lck;
872 : struct smb2_lock_element el[1];
873 :
874 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
875 : 1, /* chunks */
876 : FNAME,
877 : &src_h, 4096, /* src file */
878 : SEC_RIGHTS_FILE_ALL,
879 : FNAME2,
880 : &dest_h, 0, /* dest file */
881 : SEC_RIGHTS_FILE_ALL,
882 : &cc_copy,
883 : &ioctl);
884 7 : if (!ok) {
885 1 : torture_fail(torture, "setup copy chunk error");
886 : }
887 :
888 6 : cc_copy.chunks[0].source_off = 0;
889 6 : cc_copy.chunks[0].target_off = 0;
890 6 : cc_copy.chunks[0].length = 4096;
891 :
892 : /* open and lock the copychunk src file */
893 6 : status = torture_smb2_testfile(tree, FNAME, &src_h2);
894 6 : torture_assert_ntstatus_ok(torture, status, "2nd src open");
895 :
896 6 : lck.in.lock_count = 0x0001;
897 6 : lck.in.lock_sequence = 0x00000000;
898 6 : lck.in.file.handle = src_h2;
899 6 : lck.in.locks = el;
900 6 : el[0].offset = cc_copy.chunks[0].source_off;
901 6 : el[0].length = cc_copy.chunks[0].length;
902 6 : el[0].reserved = 0;
903 6 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
904 :
905 6 : status = smb2_lock(tree, &lck);
906 6 : torture_assert_ntstatus_ok(torture, status, "lock");
907 :
908 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
909 : &cc_copy,
910 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
911 6 : torture_assert_ndr_success(torture, ndr_ret,
912 : "ndr_push_srv_copychunk_copy");
913 :
914 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
915 : /*
916 : * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
917 : *
918 : * Edgar Olougouna @ MS wrote:
919 : * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
920 : * discrepancy observed between Windows versions, we confirm that the
921 : * behavior change is expected.
922 : *
923 : * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
924 : * to move the chunks from the source to the destination.
925 : * These ReadFile/WriteFile APIs go through the byte-range lock checks,
926 : * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
927 : *
928 : * Prior to Windows Server 2012, CopyChunk used mapped sections to move
929 : * the data. And byte range locks are not enforced on mapped I/O, and
930 : * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
931 : */
932 6 : torture_assert_ntstatus_equal(torture, status,
933 : NT_STATUS_FILE_LOCK_CONFLICT,
934 : "FSCTL_SRV_COPYCHUNK locked");
935 :
936 : /* should get cc response data with the lock conflict status */
937 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
938 : &cc_rsp,
939 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
940 6 : torture_assert_ndr_success(torture, ndr_ret,
941 : "ndr_pull_srv_copychunk_rsp");
942 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
943 : 0, /* chunks written */
944 : 0, /* chunk bytes unsuccessfully written */
945 : 0); /* total bytes written */
946 :
947 6 : lck.in.lock_count = 0x0001;
948 6 : lck.in.lock_sequence = 0x00000001;
949 6 : lck.in.file.handle = src_h2;
950 6 : lck.in.locks = el;
951 6 : el[0].offset = cc_copy.chunks[0].source_off;
952 6 : el[0].length = cc_copy.chunks[0].length;
953 6 : el[0].reserved = 0;
954 6 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
955 6 : status = smb2_lock(tree, &lck);
956 6 : torture_assert_ntstatus_ok(torture, status, "unlock");
957 :
958 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
959 6 : torture_assert_ntstatus_ok(torture, status,
960 : "FSCTL_SRV_COPYCHUNK unlocked");
961 :
962 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
963 : &cc_rsp,
964 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
965 6 : torture_assert_ndr_success(torture, ndr_ret,
966 : "ndr_pull_srv_copychunk_rsp");
967 :
968 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
969 : 1, /* chunks written */
970 : 0, /* chunk bytes unsuccessfully written */
971 : 4096); /* total bytes written */
972 6 : if (!ok) {
973 0 : torture_fail(torture, "bad copy chunk response data");
974 : }
975 :
976 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
977 6 : if (!ok) {
978 0 : torture_fail(torture, "inconsistent file data");
979 : }
980 :
981 6 : smb2_util_close(tree, src_h2);
982 6 : smb2_util_close(tree, src_h);
983 6 : smb2_util_close(tree, dest_h);
984 6 : talloc_free(tmp_ctx);
985 6 : return true;
986 : }
987 :
988 7 : static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
989 : struct smb2_tree *tree)
990 : {
991 : struct smb2_handle src_h;
992 : struct smb2_handle dest_h;
993 : struct smb2_handle dest_h2;
994 : NTSTATUS status;
995 : union smb_ioctl ioctl;
996 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
997 : struct srv_copychunk_copy cc_copy;
998 : struct srv_copychunk_rsp cc_rsp;
999 : enum ndr_err_code ndr_ret;
1000 : bool ok;
1001 : struct smb2_lock lck;
1002 : struct smb2_lock_element el[1];
1003 :
1004 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1005 : 1, /* chunks */
1006 : FNAME,
1007 : &src_h, 4096, /* src file */
1008 : SEC_RIGHTS_FILE_ALL,
1009 : FNAME2,
1010 : &dest_h, 4096, /* dest file */
1011 : SEC_RIGHTS_FILE_ALL,
1012 : &cc_copy,
1013 : &ioctl);
1014 7 : if (!ok) {
1015 1 : torture_fail(torture, "setup copy chunk error");
1016 : }
1017 :
1018 6 : cc_copy.chunks[0].source_off = 0;
1019 6 : cc_copy.chunks[0].target_off = 0;
1020 6 : cc_copy.chunks[0].length = 4096;
1021 :
1022 : /* open and lock the copychunk dest file */
1023 6 : status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
1024 6 : torture_assert_ntstatus_ok(torture, status, "2nd src open");
1025 :
1026 6 : lck.in.lock_count = 0x0001;
1027 6 : lck.in.lock_sequence = 0x00000000;
1028 6 : lck.in.file.handle = dest_h2;
1029 6 : lck.in.locks = el;
1030 6 : el[0].offset = cc_copy.chunks[0].target_off;
1031 6 : el[0].length = cc_copy.chunks[0].length;
1032 6 : el[0].reserved = 0;
1033 6 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1034 :
1035 6 : status = smb2_lock(tree, &lck);
1036 6 : torture_assert_ntstatus_ok(torture, status, "lock");
1037 :
1038 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1039 : &cc_copy,
1040 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1041 6 : torture_assert_ndr_success(torture, ndr_ret,
1042 : "ndr_push_srv_copychunk_copy");
1043 :
1044 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1045 6 : torture_assert_ntstatus_equal(torture, status,
1046 : NT_STATUS_FILE_LOCK_CONFLICT,
1047 : "FSCTL_SRV_COPYCHUNK locked");
1048 :
1049 6 : lck.in.lock_count = 0x0001;
1050 6 : lck.in.lock_sequence = 0x00000001;
1051 6 : lck.in.file.handle = dest_h2;
1052 6 : lck.in.locks = el;
1053 6 : el[0].offset = cc_copy.chunks[0].target_off;
1054 6 : el[0].length = cc_copy.chunks[0].length;
1055 6 : el[0].reserved = 0;
1056 6 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1057 6 : status = smb2_lock(tree, &lck);
1058 6 : torture_assert_ntstatus_ok(torture, status, "unlock");
1059 :
1060 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1061 6 : torture_assert_ntstatus_ok(torture, status,
1062 : "FSCTL_SRV_COPYCHUNK unlocked");
1063 :
1064 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1065 : &cc_rsp,
1066 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1067 6 : torture_assert_ndr_success(torture, ndr_ret,
1068 : "ndr_pull_srv_copychunk_rsp");
1069 :
1070 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1071 : 1, /* chunks written */
1072 : 0, /* chunk bytes unsuccessfully written */
1073 : 4096); /* total bytes written */
1074 6 : if (!ok) {
1075 0 : torture_fail(torture, "bad copy chunk response data");
1076 : }
1077 :
1078 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1079 6 : if (!ok) {
1080 0 : torture_fail(torture, "inconsistent file data");
1081 : }
1082 :
1083 6 : smb2_util_close(tree, dest_h2);
1084 6 : smb2_util_close(tree, src_h);
1085 6 : smb2_util_close(tree, dest_h);
1086 6 : talloc_free(tmp_ctx);
1087 6 : return true;
1088 : }
1089 :
1090 7 : static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
1091 : struct smb2_tree *tree)
1092 : {
1093 : struct smb2_handle src_h;
1094 : struct smb2_handle dest_h;
1095 : NTSTATUS status;
1096 : union smb_ioctl ioctl;
1097 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1098 : struct srv_copychunk_copy cc_copy;
1099 : enum ndr_err_code ndr_ret;
1100 : bool ok;
1101 :
1102 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1103 : 1,
1104 : FNAME,
1105 : &src_h, 4096,
1106 : SEC_RIGHTS_FILE_ALL,
1107 : FNAME2,
1108 : &dest_h, 0,
1109 : SEC_RIGHTS_FILE_ALL,
1110 : &cc_copy,
1111 : &ioctl);
1112 7 : if (!ok) {
1113 1 : torture_fail(torture, "setup copy chunk error");
1114 : }
1115 :
1116 : /* overwrite the resume key with a bogus value */
1117 6 : memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1118 :
1119 6 : cc_copy.chunks[0].source_off = 0;
1120 6 : cc_copy.chunks[0].target_off = 0;
1121 6 : cc_copy.chunks[0].length = 4096;
1122 :
1123 6 : 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 6 : torture_assert_ndr_success(torture, ndr_ret,
1127 : "ndr_push_srv_copychunk_copy");
1128 :
1129 : /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1130 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1131 6 : torture_assert_ntstatus_equal(torture, status,
1132 : NT_STATUS_OBJECT_NAME_NOT_FOUND,
1133 : "FSCTL_SRV_COPYCHUNK");
1134 :
1135 6 : smb2_util_close(tree, src_h);
1136 6 : smb2_util_close(tree, dest_h);
1137 6 : talloc_free(tmp_ctx);
1138 6 : return true;
1139 : }
1140 :
1141 7 : static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1142 : struct smb2_tree *tree)
1143 : {
1144 : struct smb2_handle src_h;
1145 : struct smb2_handle dest_h;
1146 : NTSTATUS status;
1147 : union smb_ioctl ioctl;
1148 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1149 : struct srv_copychunk_copy cc_copy;
1150 : struct srv_copychunk_rsp cc_rsp;
1151 : enum ndr_err_code ndr_ret;
1152 : bool ok;
1153 :
1154 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1155 : 1,
1156 : FNAME,
1157 : &src_h, 8192,
1158 : SEC_RIGHTS_FILE_ALL,
1159 : FNAME2,
1160 : &dest_h, 0,
1161 : SEC_RIGHTS_FILE_ALL,
1162 : &cc_copy,
1163 : &ioctl);
1164 7 : if (!ok) {
1165 1 : torture_fail(torture, "setup copy chunk error");
1166 : }
1167 :
1168 : /* the source is also the destination */
1169 6 : ioctl.smb2.in.file.handle = src_h;
1170 :
1171 : /* non-overlapping */
1172 6 : cc_copy.chunks[0].source_off = 0;
1173 6 : cc_copy.chunks[0].target_off = 4096;
1174 6 : cc_copy.chunks[0].length = 4096;
1175 :
1176 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1177 : &cc_copy,
1178 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1179 6 : torture_assert_ndr_success(torture, ndr_ret,
1180 : "ndr_push_srv_copychunk_copy");
1181 :
1182 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1183 6 : torture_assert_ntstatus_ok(torture, status,
1184 : "FSCTL_SRV_COPYCHUNK");
1185 :
1186 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1187 : &cc_rsp,
1188 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1189 6 : torture_assert_ndr_success(torture, ndr_ret,
1190 : "ndr_pull_srv_copychunk_rsp");
1191 :
1192 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1193 : 1, /* chunks written */
1194 : 0, /* chunk bytes unsuccessfully written */
1195 : 4096); /* total bytes written */
1196 6 : if (!ok) {
1197 0 : torture_fail(torture, "bad copy chunk response data");
1198 : }
1199 :
1200 6 : ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1201 6 : if (!ok) {
1202 0 : torture_fail(torture, "inconsistent file data");
1203 : }
1204 6 : ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1205 6 : if (!ok) {
1206 0 : torture_fail(torture, "inconsistent file data");
1207 : }
1208 :
1209 6 : smb2_util_close(tree, src_h);
1210 6 : smb2_util_close(tree, dest_h);
1211 6 : talloc_free(tmp_ctx);
1212 6 : return true;
1213 : }
1214 :
1215 : /*
1216 : * Test a single-chunk copychunk request, where the source and target ranges
1217 : * overlap, and the SourceKey refers to the same target file. E.g:
1218 : *
1219 : * Initial State
1220 : * -------------
1221 : * File: src_and_dest
1222 : * Offset: 0123456789
1223 : * Data: abcdefghij
1224 : *
1225 : * Request
1226 : * -------
1227 : * FSCTL_SRV_COPYCHUNK(src_and_dest)
1228 : * SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1229 : * ChunkCount = 1
1230 : * Chunks[0].SourceOffset = 0
1231 : * Chunks[0].TargetOffset = 4
1232 : * Chunks[0].Length = 6
1233 : *
1234 : * Resultant State
1235 : * ---------------
1236 : * File: src_and_dest
1237 : * Offset: 0123456789
1238 : * Data: abcdabcdef
1239 : *
1240 : * The resultant contents of src_and_dest is dependent on the server's
1241 : * copy algorithm. In the above example, the server uses an IO buffer
1242 : * large enough to hold the entire six-byte source data before writing
1243 : * to TargetOffset. If the server were to use a four-byte IO buffer and
1244 : * started reads/writes from the lowest offset, then the two overlapping
1245 : * bytes in the above example would be overwritten before being read. The
1246 : * resultant file contents would be abcdabcdab.
1247 : *
1248 : * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1249 : * after this offset are written before being read. Windows 2012 on the
1250 : * other hand appears to use a buffer large enough to hold its maximum
1251 : * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1252 : * default (vfs_cc_state.buf).
1253 : *
1254 : * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1255 : * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1256 : * to use a different copy algorithm to 2008r2.
1257 : */
1258 : static bool
1259 7 : test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1260 : struct smb2_tree *tree)
1261 : {
1262 : struct smb2_handle src_h;
1263 : struct smb2_handle dest_h;
1264 : NTSTATUS status;
1265 : union smb_ioctl ioctl;
1266 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1267 : struct srv_copychunk_copy cc_copy;
1268 : struct srv_copychunk_rsp cc_rsp;
1269 : enum ndr_err_code ndr_ret;
1270 : bool ok;
1271 :
1272 : /* exceed the vfs_default copy buffer */
1273 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1274 : 1,
1275 : FNAME,
1276 : &src_h, 2048 * 2,
1277 : SEC_RIGHTS_FILE_ALL,
1278 : FNAME2,
1279 : &dest_h, 0,
1280 : SEC_RIGHTS_FILE_ALL,
1281 : &cc_copy,
1282 : &ioctl);
1283 7 : if (!ok) {
1284 1 : torture_fail(torture, "setup copy chunk error");
1285 : }
1286 :
1287 : /* the source is also the destination */
1288 6 : ioctl.smb2.in.file.handle = src_h;
1289 :
1290 : /* 8 bytes overlap between source and target ranges */
1291 6 : cc_copy.chunks[0].source_off = 0;
1292 6 : cc_copy.chunks[0].target_off = 2048 - 8;
1293 6 : cc_copy.chunks[0].length = 2048;
1294 :
1295 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1296 : &cc_copy,
1297 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1298 6 : torture_assert_ndr_success(torture, ndr_ret,
1299 : "ndr_push_srv_copychunk_copy");
1300 :
1301 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1302 6 : torture_assert_ntstatus_ok(torture, status,
1303 : "FSCTL_SRV_COPYCHUNK");
1304 :
1305 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1306 : &cc_rsp,
1307 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1308 6 : torture_assert_ndr_success(torture, ndr_ret,
1309 : "ndr_pull_srv_copychunk_rsp");
1310 :
1311 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1312 : 1, /* chunks written */
1313 : 0, /* chunk bytes unsuccessfully written */
1314 : 2048); /* total bytes written */
1315 6 : if (!ok) {
1316 0 : torture_fail(torture, "bad copy chunk response data");
1317 : }
1318 :
1319 6 : ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1320 6 : if (!ok) {
1321 0 : torture_fail(torture, "inconsistent file data");
1322 : }
1323 6 : ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1324 6 : if (!ok) {
1325 0 : torture_fail(torture, "inconsistent file data");
1326 : }
1327 :
1328 6 : smb2_util_close(tree, src_h);
1329 6 : smb2_util_close(tree, dest_h);
1330 6 : talloc_free(tmp_ctx);
1331 6 : return true;
1332 : }
1333 :
1334 7 : static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1335 : struct smb2_tree *tree)
1336 : {
1337 : struct smb2_handle src_h;
1338 : struct smb2_handle dest_h;
1339 : NTSTATUS status;
1340 : union smb_ioctl ioctl;
1341 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1342 : struct srv_copychunk_copy cc_copy;
1343 : enum ndr_err_code ndr_ret;
1344 : bool ok;
1345 : /* read permission on src */
1346 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1347 : FNAME, &src_h, 4096, /* fill 4096 byte src file */
1348 : SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1349 : FNAME2, &dest_h, 0, /* 0 byte dest file */
1350 : SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1351 7 : if (!ok) {
1352 1 : torture_fail(torture, "setup copy chunk error");
1353 : }
1354 :
1355 6 : cc_copy.chunks[0].source_off = 0;
1356 6 : cc_copy.chunks[0].target_off = 0;
1357 6 : cc_copy.chunks[0].length = 4096;
1358 :
1359 6 : ndr_ret = ndr_push_struct_blob(
1360 : &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1361 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1362 6 : torture_assert_ndr_success(torture, ndr_ret,
1363 : "ndr_push_srv_copychunk_copy");
1364 :
1365 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1366 6 : torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1367 : "FSCTL_SRV_COPYCHUNK");
1368 :
1369 6 : smb2_util_close(tree, src_h);
1370 6 : smb2_util_close(tree, dest_h);
1371 :
1372 : /* execute permission on src */
1373 6 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1374 : FNAME, &src_h, 4096, /* fill 4096 byte src file */
1375 : SEC_FILE_EXECUTE | SEC_FILE_READ_ATTRIBUTE,
1376 : FNAME2, &dest_h, 0, /* 0 byte dest file */
1377 : SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1378 6 : if (!ok) {
1379 0 : torture_fail(torture, "setup copy chunk error");
1380 : }
1381 :
1382 6 : cc_copy.chunks[0].source_off = 0;
1383 6 : cc_copy.chunks[0].target_off = 0;
1384 6 : cc_copy.chunks[0].length = 4096;
1385 :
1386 6 : ndr_ret = ndr_push_struct_blob(
1387 : &ioctl.smb2.in.out, tmp_ctx, &cc_copy,
1388 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1389 6 : torture_assert_ndr_success(torture, ndr_ret,
1390 : "ndr_push_srv_copychunk_copy");
1391 :
1392 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1393 6 : torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
1394 : "FSCTL_SRV_COPYCHUNK");
1395 :
1396 6 : smb2_util_close(tree, src_h);
1397 6 : smb2_util_close(tree, dest_h);
1398 :
1399 : /* neither read nor execute permission on src */
1400 6 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1401 : FNAME, &src_h, 4096, /* fill 4096 byte src file */
1402 : SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1403 : 0, /* 0 byte dest file */
1404 : SEC_RIGHTS_FILE_ALL, &cc_copy, &ioctl);
1405 6 : if (!ok) {
1406 0 : torture_fail(torture, "setup copy chunk error");
1407 : }
1408 :
1409 6 : cc_copy.chunks[0].source_off = 0;
1410 6 : cc_copy.chunks[0].target_off = 0;
1411 6 : cc_copy.chunks[0].length = 4096;
1412 :
1413 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1414 : &cc_copy,
1415 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1416 6 : torture_assert_ndr_success(torture, ndr_ret,
1417 : "ndr_push_srv_copychunk_copy");
1418 :
1419 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1420 6 : torture_assert_ntstatus_equal(torture, status,
1421 : NT_STATUS_ACCESS_DENIED,
1422 : "FSCTL_SRV_COPYCHUNK");
1423 :
1424 6 : smb2_util_close(tree, src_h);
1425 6 : smb2_util_close(tree, dest_h);
1426 :
1427 : /* no write permission on dest */
1428 6 : ok = test_setup_copy_chunk(
1429 : torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1430 : FNAME, &src_h, 4096, /* fill 4096 byte src file */
1431 : SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE, FNAME2, &dest_h,
1432 : 0, /* 0 byte dest file */
1433 : (SEC_RIGHTS_FILE_ALL &
1434 : ~(SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)),
1435 : &cc_copy, &ioctl);
1436 6 : if (!ok) {
1437 0 : torture_fail(torture, "setup copy chunk error");
1438 : }
1439 :
1440 6 : cc_copy.chunks[0].source_off = 0;
1441 6 : cc_copy.chunks[0].target_off = 0;
1442 6 : cc_copy.chunks[0].length = 4096;
1443 :
1444 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1445 : &cc_copy,
1446 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1447 6 : torture_assert_ndr_success(torture, ndr_ret,
1448 : "ndr_push_srv_copychunk_copy");
1449 :
1450 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1451 6 : torture_assert_ntstatus_equal(torture, status,
1452 : NT_STATUS_ACCESS_DENIED,
1453 : "FSCTL_SRV_COPYCHUNK");
1454 :
1455 6 : smb2_util_close(tree, src_h);
1456 6 : smb2_util_close(tree, dest_h);
1457 :
1458 : /* no read permission on dest */
1459 6 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx, 1, /* 1 chunk */
1460 : FNAME, &src_h, 4096, /* fill 4096 byte src file */
1461 : SEC_FILE_READ_DATA | SEC_FILE_READ_ATTRIBUTE,
1462 : FNAME2, &dest_h, 0, /* 0 byte dest file */
1463 : (SEC_RIGHTS_FILE_ALL & ~SEC_FILE_READ_DATA),
1464 : &cc_copy, &ioctl);
1465 6 : if (!ok) {
1466 0 : torture_fail(torture, "setup copy chunk error");
1467 : }
1468 :
1469 6 : cc_copy.chunks[0].source_off = 0;
1470 6 : cc_copy.chunks[0].target_off = 0;
1471 6 : cc_copy.chunks[0].length = 4096;
1472 :
1473 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1474 : &cc_copy,
1475 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1476 6 : torture_assert_ndr_success(torture, ndr_ret,
1477 : "ndr_push_srv_copychunk_copy");
1478 :
1479 : /*
1480 : * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1481 : * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1482 : */
1483 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1484 6 : torture_assert_ntstatus_equal(torture, status,
1485 : NT_STATUS_ACCESS_DENIED,
1486 : "FSCTL_SRV_COPYCHUNK");
1487 :
1488 6 : smb2_util_close(tree, src_h);
1489 6 : smb2_util_close(tree, dest_h);
1490 6 : talloc_free(tmp_ctx);
1491 :
1492 6 : return true;
1493 : }
1494 :
1495 7 : static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1496 : struct smb2_tree *tree)
1497 : {
1498 : struct smb2_handle src_h;
1499 : struct smb2_handle dest_h;
1500 : NTSTATUS status;
1501 : union smb_ioctl ioctl;
1502 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1503 : struct srv_copychunk_copy cc_copy;
1504 : enum ndr_err_code ndr_ret;
1505 : bool ok;
1506 :
1507 : /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1508 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1509 : 1, /* 1 chunk */
1510 : FNAME,
1511 : &src_h, 4096, /* fill 4096 byte src file */
1512 : SEC_RIGHTS_FILE_ALL,
1513 : FNAME2,
1514 : &dest_h, 0, /* 0 byte dest file */
1515 : (SEC_RIGHTS_FILE_WRITE
1516 : | SEC_RIGHTS_FILE_EXECUTE),
1517 : &cc_copy,
1518 : &ioctl);
1519 7 : if (!ok) {
1520 1 : torture_fail(torture, "setup copy chunk error");
1521 : }
1522 :
1523 6 : ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1524 6 : cc_copy.chunks[0].source_off = 0;
1525 6 : cc_copy.chunks[0].target_off = 0;
1526 6 : cc_copy.chunks[0].length = 4096;
1527 :
1528 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1529 : &cc_copy,
1530 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1531 6 : torture_assert_ndr_success(torture, ndr_ret,
1532 : "ndr_push_srv_copychunk_copy");
1533 :
1534 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1535 6 : torture_assert_ntstatus_ok(torture, status,
1536 : "FSCTL_SRV_COPYCHUNK_WRITE");
1537 :
1538 6 : smb2_util_close(tree, src_h);
1539 6 : smb2_util_close(tree, dest_h);
1540 6 : talloc_free(tmp_ctx);
1541 :
1542 6 : return true;
1543 : }
1544 :
1545 7 : static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1546 : struct smb2_tree *tree)
1547 : {
1548 : struct smb2_handle src_h;
1549 : struct smb2_handle dest_h;
1550 : NTSTATUS status;
1551 : union smb_ioctl ioctl;
1552 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1553 : struct srv_copychunk_copy cc_copy;
1554 : struct srv_copychunk_rsp cc_rsp;
1555 : enum ndr_err_code ndr_ret;
1556 : bool ok;
1557 :
1558 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1559 : 1, /* 1 chunk */
1560 : FNAME,
1561 : &src_h, 4096, /* fill 4096 byte src file */
1562 : SEC_RIGHTS_FILE_ALL,
1563 : FNAME2,
1564 : &dest_h, 0, /* 0 byte dest file */
1565 : SEC_RIGHTS_FILE_ALL,
1566 : &cc_copy,
1567 : &ioctl);
1568 7 : if (!ok) {
1569 1 : torture_fail(torture, "setup copy chunk error");
1570 : }
1571 :
1572 : /* Request copy where off + length exceeds size of src */
1573 6 : cc_copy.chunks[0].source_off = 1024;
1574 6 : cc_copy.chunks[0].target_off = 0;
1575 6 : cc_copy.chunks[0].length = 4096;
1576 :
1577 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1578 : &cc_copy,
1579 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1580 6 : torture_assert_ndr_success(torture, ndr_ret,
1581 : "ndr_push_srv_copychunk_copy");
1582 :
1583 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1584 6 : torture_assert_ntstatus_equal(torture, status,
1585 : NT_STATUS_INVALID_VIEW_SIZE,
1586 : "FSCTL_SRV_COPYCHUNK oversize");
1587 :
1588 : /* Request copy where length exceeds size of src */
1589 6 : cc_copy.chunks[0].source_off = 1024;
1590 6 : cc_copy.chunks[0].target_off = 0;
1591 6 : cc_copy.chunks[0].length = 3072;
1592 :
1593 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1594 : &cc_copy,
1595 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1596 6 : torture_assert_ndr_success(torture, ndr_ret,
1597 : "ndr_push_srv_copychunk_copy");
1598 :
1599 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1600 6 : torture_assert_ntstatus_ok(torture, status,
1601 : "FSCTL_SRV_COPYCHUNK just right");
1602 :
1603 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1604 : &cc_rsp,
1605 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1606 6 : torture_assert_ndr_success(torture, ndr_ret,
1607 : "ndr_pull_srv_copychunk_rsp");
1608 :
1609 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1610 : 1, /* chunks written */
1611 : 0, /* chunk bytes unsuccessfully written */
1612 : 3072); /* total bytes written */
1613 6 : if (!ok) {
1614 0 : torture_fail(torture, "bad copy chunk response data");
1615 : }
1616 :
1617 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1618 6 : if (!ok) {
1619 0 : torture_fail(torture, "inconsistent file data");
1620 : }
1621 :
1622 6 : smb2_util_close(tree, src_h);
1623 6 : smb2_util_close(tree, dest_h);
1624 6 : talloc_free(tmp_ctx);
1625 6 : return true;
1626 : }
1627 :
1628 : static bool
1629 7 : test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1630 : struct smb2_tree *tree)
1631 : {
1632 : struct smb2_handle src_h;
1633 : struct smb2_handle dest_h;
1634 : NTSTATUS status;
1635 : union smb_ioctl ioctl;
1636 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1637 : struct srv_copychunk_copy cc_copy;
1638 : struct srv_copychunk_rsp cc_rsp;
1639 : enum ndr_err_code ndr_ret;
1640 : bool ok;
1641 :
1642 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1643 : 2, /* 2 chunks */
1644 : FNAME,
1645 : &src_h, 8192, /* fill 8192 byte src file */
1646 : SEC_RIGHTS_FILE_ALL,
1647 : FNAME2,
1648 : &dest_h, 0, /* 0 byte dest file */
1649 : SEC_RIGHTS_FILE_ALL,
1650 : &cc_copy,
1651 : &ioctl);
1652 7 : if (!ok) {
1653 1 : torture_fail(torture, "setup copy chunk error");
1654 : }
1655 :
1656 : /* Request copy where off + length exceeds size of src */
1657 6 : cc_copy.chunks[0].source_off = 0;
1658 6 : cc_copy.chunks[0].target_off = 0;
1659 6 : cc_copy.chunks[0].length = 4096;
1660 :
1661 6 : cc_copy.chunks[1].source_off = 4096;
1662 6 : cc_copy.chunks[1].target_off = 4096;
1663 6 : cc_copy.chunks[1].length = 8192;
1664 :
1665 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1666 : &cc_copy,
1667 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1668 6 : torture_assert_ndr_success(torture, ndr_ret,
1669 : "ndr_push_srv_copychunk_copy");
1670 :
1671 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1672 6 : torture_assert_ntstatus_equal(torture, status,
1673 : NT_STATUS_INVALID_VIEW_SIZE,
1674 : "FSCTL_SRV_COPYCHUNK oversize");
1675 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1676 : &cc_rsp,
1677 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1678 6 : torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1679 :
1680 : /* first chunk should still be written */
1681 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1682 : 1, /* chunks written */
1683 : 0, /* chunk bytes unsuccessfully written */
1684 : 4096); /* total bytes written */
1685 6 : if (!ok) {
1686 0 : torture_fail(torture, "bad copy chunk response data");
1687 : }
1688 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1689 6 : if (!ok) {
1690 0 : torture_fail(torture, "inconsistent file data");
1691 : }
1692 :
1693 6 : smb2_util_close(tree, src_h);
1694 6 : smb2_util_close(tree, dest_h);
1695 6 : talloc_free(tmp_ctx);
1696 6 : return true;
1697 : }
1698 :
1699 7 : static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1700 : struct smb2_tree *tree)
1701 : {
1702 : struct smb2_handle src_h;
1703 : struct smb2_handle dest_h;
1704 : NTSTATUS status;
1705 : union smb_ioctl ioctl;
1706 : struct smb2_read r;
1707 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1708 : struct srv_copychunk_copy cc_copy;
1709 : struct srv_copychunk_rsp cc_rsp;
1710 : enum ndr_err_code ndr_ret;
1711 : bool ok;
1712 : int i;
1713 :
1714 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1715 : 1, /* 1 chunk */
1716 : FNAME,
1717 : &src_h, 4096, /* fill 4096 byte src file */
1718 : SEC_RIGHTS_FILE_ALL,
1719 : FNAME2,
1720 : &dest_h, 0, /* 0 byte dest file */
1721 : SEC_RIGHTS_FILE_ALL,
1722 : &cc_copy,
1723 : &ioctl);
1724 7 : if (!ok) {
1725 1 : torture_fail(torture, "setup copy chunk error");
1726 : }
1727 :
1728 : /* copy all src file data (via a single chunk desc) */
1729 6 : cc_copy.chunks[0].source_off = 0;
1730 6 : cc_copy.chunks[0].target_off = 4096;
1731 6 : cc_copy.chunks[0].length = 4096;
1732 :
1733 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1734 : &cc_copy,
1735 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1736 6 : torture_assert_ndr_success(torture, ndr_ret,
1737 : "ndr_push_srv_copychunk_copy");
1738 :
1739 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1740 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1741 :
1742 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1743 : &cc_rsp,
1744 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1745 6 : torture_assert_ndr_success(torture, ndr_ret,
1746 : "ndr_pull_srv_copychunk_rsp");
1747 :
1748 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1749 : 1, /* chunks written */
1750 : 0, /* chunk bytes unsuccessfully written */
1751 : 4096); /* total bytes written */
1752 6 : if (!ok) {
1753 0 : torture_fail(torture, "bad copy chunk response data");
1754 : }
1755 :
1756 : /* check for zeros in first 4k */
1757 6 : ZERO_STRUCT(r);
1758 6 : r.in.file.handle = dest_h;
1759 6 : r.in.length = 4096;
1760 6 : r.in.offset = 0;
1761 6 : status = smb2_read(tree, tmp_ctx, &r);
1762 6 : torture_assert_ntstatus_ok(torture, status, "read");
1763 :
1764 6 : torture_assert_u64_equal(torture, r.out.data.length, 4096,
1765 : "read data len mismatch");
1766 :
1767 24582 : for (i = 0; i < 4096; i++) {
1768 24576 : torture_assert(torture, (r.out.data.data[i] == 0),
1769 : "sparse did not pass class");
1770 : }
1771 :
1772 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1773 6 : if (!ok) {
1774 0 : torture_fail(torture, "inconsistent file data");
1775 : }
1776 :
1777 6 : smb2_util_close(tree, src_h);
1778 6 : smb2_util_close(tree, dest_h);
1779 6 : talloc_free(tmp_ctx);
1780 6 : return true;
1781 : }
1782 :
1783 : /*
1784 : * set the ioctl MaxOutputResponse size to less than
1785 : * sizeof(struct srv_copychunk_rsp)
1786 : */
1787 7 : static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1788 : struct smb2_tree *tree)
1789 : {
1790 : struct smb2_handle src_h;
1791 : struct smb2_handle dest_h;
1792 : NTSTATUS status;
1793 : union smb_ioctl ioctl;
1794 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1795 : struct srv_copychunk_copy cc_copy;
1796 : enum ndr_err_code ndr_ret;
1797 : bool ok;
1798 :
1799 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1800 : 1, /* 1 chunk */
1801 : FNAME,
1802 : &src_h, 4096, /* fill 4096 byte src file */
1803 : SEC_RIGHTS_FILE_ALL,
1804 : FNAME2,
1805 : &dest_h, 0, /* 0 byte dest file */
1806 : SEC_RIGHTS_FILE_ALL,
1807 : &cc_copy,
1808 : &ioctl);
1809 7 : if (!ok) {
1810 1 : torture_fail(torture, "setup copy chunk error");
1811 : }
1812 :
1813 6 : cc_copy.chunks[0].source_off = 0;
1814 6 : cc_copy.chunks[0].target_off = 0;
1815 6 : cc_copy.chunks[0].length = 4096;
1816 : /* req is valid, but use undersize max_output_response */
1817 6 : ioctl.smb2.in.max_output_response = sizeof(struct srv_copychunk_rsp) - 1;
1818 :
1819 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1820 : &cc_copy,
1821 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1822 6 : torture_assert_ndr_success(torture, ndr_ret,
1823 : "ndr_push_srv_copychunk_copy");
1824 :
1825 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1826 6 : torture_assert_ntstatus_equal(torture, status,
1827 : NT_STATUS_INVALID_PARAMETER,
1828 : "FSCTL_SRV_COPYCHUNK");
1829 :
1830 6 : smb2_util_close(tree, src_h);
1831 6 : smb2_util_close(tree, dest_h);
1832 6 : talloc_free(tmp_ctx);
1833 6 : return true;
1834 : }
1835 :
1836 7 : static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1837 : struct smb2_tree *tree)
1838 : {
1839 : struct smb2_handle src_h;
1840 : struct smb2_handle dest_h;
1841 : NTSTATUS status;
1842 : union smb_ioctl ioctl;
1843 : union smb_fileinfo q;
1844 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
1845 : struct srv_copychunk_copy cc_copy;
1846 : struct srv_copychunk_rsp cc_rsp;
1847 : enum ndr_err_code ndr_ret;
1848 : bool ok;
1849 :
1850 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1851 : 1, /* 1 chunk */
1852 : FNAME,
1853 : &src_h, 4096, /* fill 4096 byte src file */
1854 : SEC_RIGHTS_FILE_ALL,
1855 : FNAME2,
1856 : &dest_h, 0, /* 0 byte dest file */
1857 : SEC_RIGHTS_FILE_ALL,
1858 : &cc_copy,
1859 : &ioctl);
1860 7 : if (!ok) {
1861 1 : torture_fail(torture, "setup copy chunk error");
1862 : }
1863 :
1864 : /* zero length server-side copy (via a single chunk desc) */
1865 6 : cc_copy.chunks[0].source_off = 0;
1866 6 : cc_copy.chunks[0].target_off = 0;
1867 6 : cc_copy.chunks[0].length = 0;
1868 :
1869 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1870 : &cc_copy,
1871 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1872 6 : torture_assert_ndr_success(torture, ndr_ret,
1873 : "ndr_push_srv_copychunk_copy");
1874 :
1875 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1876 6 : torture_assert_ntstatus_equal(torture, status,
1877 : NT_STATUS_INVALID_PARAMETER,
1878 : "bad zero-length chunk response");
1879 :
1880 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1881 : &cc_rsp,
1882 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1883 6 : torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1884 :
1885 6 : ZERO_STRUCT(q);
1886 6 : q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1887 6 : q.all_info2.in.file.handle = dest_h;
1888 6 : status = smb2_getinfo_file(tree, torture, &q);
1889 6 : torture_assert_ntstatus_ok(torture, status, "getinfo");
1890 :
1891 6 : torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1892 : "size after zero len clone");
1893 :
1894 6 : smb2_util_close(tree, src_h);
1895 6 : smb2_util_close(tree, dest_h);
1896 6 : talloc_free(tmp_ctx);
1897 6 : return true;
1898 : }
1899 :
1900 7 : static bool copy_one_stream(struct torture_context *torture,
1901 : struct smb2_tree *tree,
1902 : TALLOC_CTX *tmp_ctx,
1903 : const char *src_sname,
1904 : const char *dst_sname)
1905 : {
1906 7 : struct smb2_handle src_h = {{0}};
1907 7 : struct smb2_handle dest_h = {{0}};
1908 : NTSTATUS status;
1909 : union smb_ioctl io;
1910 : struct srv_copychunk_copy cc_copy;
1911 : struct srv_copychunk_rsp cc_rsp;
1912 : enum ndr_err_code ndr_ret;
1913 7 : bool ok = false;
1914 :
1915 7 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
1916 : 1, /* 1 chunk */
1917 : src_sname,
1918 : &src_h, 256, /* fill 256 byte src file */
1919 : SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1920 : dst_sname,
1921 : &dest_h, 0, /* 0 byte dest file */
1922 : SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA,
1923 : &cc_copy,
1924 : &io);
1925 7 : torture_assert_goto(torture, ok == true, ok, done,
1926 : "setup copy chunk error\n");
1927 :
1928 : /* copy all src file data (via a single chunk desc) */
1929 6 : cc_copy.chunks[0].source_off = 0;
1930 6 : cc_copy.chunks[0].target_off = 0;
1931 6 : cc_copy.chunks[0].length = 256;
1932 :
1933 6 : ndr_ret = ndr_push_struct_blob(
1934 : &io.smb2.in.out, tmp_ctx, &cc_copy,
1935 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1936 :
1937 6 : torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1938 : "ndr_push_srv_copychunk_copy\n");
1939 :
1940 6 : status = smb2_ioctl(tree, tmp_ctx, &io.smb2);
1941 6 : torture_assert_ntstatus_ok_goto(torture, status, ok, done,
1942 : "FSCTL_SRV_COPYCHUNK\n");
1943 :
1944 6 : ndr_ret = ndr_pull_struct_blob(
1945 : &io.smb2.out.out, tmp_ctx, &cc_rsp,
1946 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1947 :
1948 6 : torture_assert_ndr_success_goto(torture, ndr_ret, ok, done,
1949 : "ndr_pull_srv_copychunk_rsp\n");
1950 :
1951 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
1952 : 1, /* chunks written */
1953 : 0, /* chunk bytes unsuccessfully written */
1954 : 256); /* total bytes written */
1955 6 : torture_assert_goto(torture, ok == true, ok, done,
1956 : "bad copy chunk response data\n");
1957 :
1958 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 256, 0);
1959 6 : if (!ok) {
1960 0 : torture_fail(torture, "inconsistent file data\n");
1961 : }
1962 :
1963 6 : done:
1964 7 : if (!smb2_util_handle_empty(src_h)) {
1965 7 : smb2_util_close(tree, src_h);
1966 : }
1967 7 : if (!smb2_util_handle_empty(dest_h)) {
1968 7 : smb2_util_close(tree, dest_h);
1969 : }
1970 :
1971 7 : return ok;
1972 : }
1973 :
1974 : /**
1975 : * Create a file
1976 : **/
1977 14 : static bool torture_setup_file(TALLOC_CTX *mem_ctx,
1978 : struct smb2_tree *tree,
1979 : const char *name)
1980 : {
1981 : struct smb2_create io;
1982 : NTSTATUS status;
1983 :
1984 14 : smb2_util_unlink(tree, name);
1985 14 : ZERO_STRUCT(io);
1986 14 : io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED;
1987 14 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1988 14 : io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF;
1989 14 : io.in.share_access =
1990 : NTCREATEX_SHARE_ACCESS_DELETE|
1991 : NTCREATEX_SHARE_ACCESS_READ|
1992 : NTCREATEX_SHARE_ACCESS_WRITE;
1993 14 : io.in.create_options = 0;
1994 14 : io.in.fname = name;
1995 :
1996 14 : status = smb2_create(tree, mem_ctx, &io);
1997 14 : if (!NT_STATUS_IS_OK(status)) {
1998 0 : return false;
1999 : }
2000 :
2001 14 : status = smb2_util_close(tree, io.out.file.handle);
2002 14 : if (!NT_STATUS_IS_OK(status)) {
2003 0 : return false;
2004 : }
2005 :
2006 14 : return true;
2007 : }
2008 :
2009 7 : static bool test_copy_chunk_streams(struct torture_context *torture,
2010 : struct smb2_tree *tree)
2011 : {
2012 7 : const char *src_name = "src";
2013 7 : const char *dst_name = "dst";
2014 : struct names {
2015 : const char *src_sname;
2016 : const char *dst_sname;
2017 7 : } names[] = {
2018 : { "src:foo", "dst:foo" }
2019 : };
2020 : int i;
2021 7 : TALLOC_CTX *tmp_ctx = NULL;
2022 7 : bool ok = false;
2023 :
2024 7 : tmp_ctx = talloc_new(tree);
2025 7 : torture_assert_not_null_goto(torture, tmp_ctx, ok, done,
2026 : "torture_setup_file\n");
2027 :
2028 7 : ok = torture_setup_file(torture, tree, src_name);
2029 7 : torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
2030 7 : ok = torture_setup_file(torture, tree, dst_name);
2031 7 : torture_assert_goto(torture, ok == true, ok, done, "torture_setup_file\n");
2032 :
2033 13 : for (i = 0; i < ARRAY_SIZE(names); i++) {
2034 7 : ok = copy_one_stream(torture, tree, tmp_ctx,
2035 : names[i].src_sname,
2036 : names[i].dst_sname);
2037 7 : torture_assert_goto(torture, ok == true, ok, done,
2038 : "copy_one_stream failed\n");
2039 : }
2040 :
2041 6 : done:
2042 7 : smb2_util_unlink(tree, src_name);
2043 7 : smb2_util_unlink(tree, dst_name);
2044 7 : talloc_free(tmp_ctx);
2045 7 : return ok;
2046 : }
2047 :
2048 7 : static bool test_copy_chunk_across_shares(struct torture_context *tctx,
2049 : struct smb2_tree *tree)
2050 : {
2051 7 : TALLOC_CTX *mem_ctx = NULL;
2052 7 : struct smb2_tree *tree2 = NULL;
2053 7 : struct smb2_handle src_h = {{0}};
2054 7 : struct smb2_handle dest_h = {{0}};
2055 : union smb_ioctl ioctl;
2056 : struct srv_copychunk_copy cc_copy;
2057 : struct srv_copychunk_rsp cc_rsp;
2058 : enum ndr_err_code ndr_ret;
2059 : NTSTATUS status;
2060 7 : bool ok = false;
2061 :
2062 7 : mem_ctx = talloc_new(tctx);
2063 7 : torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2064 : "talloc_new\n");
2065 :
2066 7 : ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2067 7 : torture_assert_goto(tctx, ok == true, ok, done,
2068 : "torture_smb2_tree_connect failed\n");
2069 :
2070 7 : ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2071 : 1, /* 1 chunk */
2072 : FNAME,
2073 : &src_h, 4096, /* fill 4096 byte src file */
2074 : SEC_RIGHTS_FILE_ALL,
2075 : FNAME2,
2076 : &dest_h, 0, /* 0 byte dest file */
2077 : SEC_RIGHTS_FILE_ALL,
2078 : &cc_copy,
2079 : &ioctl);
2080 7 : torture_assert_goto(tctx, ok == true, ok, done,
2081 : "test_setup_copy_chunk failed\n");
2082 :
2083 6 : cc_copy.chunks[0].source_off = 0;
2084 6 : cc_copy.chunks[0].target_off = 0;
2085 6 : cc_copy.chunks[0].length = 4096;
2086 :
2087 6 : ndr_ret = ndr_push_struct_blob(
2088 : &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2089 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2090 6 : torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2091 : "ndr_push_srv_copychunk_copy\n");
2092 :
2093 6 : status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2094 6 : torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2095 : "FSCTL_SRV_COPYCHUNK\n");
2096 :
2097 6 : ndr_ret = ndr_pull_struct_blob(
2098 : &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2099 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2100 :
2101 6 : torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2102 : "ndr_pull_srv_copychunk_rsp\n");
2103 :
2104 6 : ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2105 : 1, /* chunks written */
2106 : 0, /* chunk bytes unsuccessfully written */
2107 : 4096); /* total bytes written */
2108 6 : torture_assert_goto(tctx, ok == true, ok, done,
2109 : "bad copy chunk response data\n");
2110 :
2111 6 : ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2112 6 : torture_assert_goto(tctx, ok == true, ok, done,
2113 : "inconsistent file data\n");
2114 :
2115 12 : done:
2116 7 : TALLOC_FREE(mem_ctx);
2117 7 : if (!smb2_util_handle_empty(src_h)) {
2118 7 : smb2_util_close(tree, src_h);
2119 : }
2120 7 : if (!smb2_util_handle_empty(dest_h)) {
2121 7 : smb2_util_close(tree2, dest_h);
2122 : }
2123 7 : smb2_util_unlink(tree, FNAME);
2124 7 : smb2_util_unlink(tree2, FNAME2);
2125 7 : if (tree2 != NULL) {
2126 7 : smb2_tdis(tree2);
2127 : }
2128 7 : return ok;
2129 : }
2130 :
2131 : /* Test closing the src handle */
2132 7 : static bool test_copy_chunk_across_shares2(struct torture_context *tctx,
2133 : struct smb2_tree *tree)
2134 : {
2135 7 : TALLOC_CTX *mem_ctx = NULL;
2136 7 : struct smb2_tree *tree2 = NULL;
2137 7 : struct smb2_handle src_h = {{0}};
2138 7 : struct smb2_handle dest_h = {{0}};
2139 : union smb_ioctl ioctl;
2140 : struct srv_copychunk_copy cc_copy;
2141 : enum ndr_err_code ndr_ret;
2142 : NTSTATUS status;
2143 7 : bool ok = false;
2144 :
2145 7 : mem_ctx = talloc_new(tctx);
2146 7 : torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2147 : "talloc_new\n");
2148 :
2149 7 : ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2150 7 : torture_assert_goto(tctx, ok == true, ok, done,
2151 : "torture_smb2_tree_connect failed\n");
2152 :
2153 7 : ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2154 : 1, /* 1 chunk */
2155 : FNAME,
2156 : &src_h, 4096, /* fill 4096 byte src file */
2157 : SEC_RIGHTS_FILE_ALL,
2158 : FNAME2,
2159 : &dest_h, 0, /* 0 byte dest file */
2160 : SEC_RIGHTS_FILE_ALL,
2161 : &cc_copy,
2162 : &ioctl);
2163 7 : torture_assert_goto(tctx, ok == true, ok, done,
2164 : "test_setup_copy_chunk failed\n");
2165 :
2166 6 : status = smb2_util_close(tree, src_h);
2167 6 : torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
2168 : "smb2_util_close failed\n");
2169 6 : ZERO_STRUCT(src_h);
2170 :
2171 6 : cc_copy.chunks[0].source_off = 0;
2172 6 : cc_copy.chunks[0].target_off = 0;
2173 6 : cc_copy.chunks[0].length = 4096;
2174 :
2175 6 : ndr_ret = ndr_push_struct_blob(
2176 : &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2177 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2178 6 : torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2179 : "ndr_push_srv_copychunk_copy\n");
2180 :
2181 6 : status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2182 6 : torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
2183 : ok, done, "smb2_ioctl failed\n");
2184 :
2185 7 : done:
2186 7 : TALLOC_FREE(mem_ctx);
2187 7 : if (!smb2_util_handle_empty(src_h)) {
2188 1 : smb2_util_close(tree, src_h);
2189 : }
2190 7 : if (!smb2_util_handle_empty(dest_h)) {
2191 7 : smb2_util_close(tree2, dest_h);
2192 : }
2193 7 : smb2_util_unlink(tree, FNAME);
2194 7 : smb2_util_unlink(tree2, FNAME2);
2195 7 : if (tree2 != NULL) {
2196 7 : smb2_tdis(tree2);
2197 : }
2198 7 : return ok;
2199 : }
2200 :
2201 : /* Test offset works */
2202 7 : static bool test_copy_chunk_across_shares3(struct torture_context *tctx,
2203 : struct smb2_tree *tree)
2204 : {
2205 7 : TALLOC_CTX *mem_ctx = NULL;
2206 7 : struct smb2_tree *tree2 = NULL;
2207 7 : struct smb2_handle src_h = {{0}};
2208 7 : struct smb2_handle dest_h = {{0}};
2209 : union smb_ioctl ioctl;
2210 : struct srv_copychunk_copy cc_copy;
2211 : struct srv_copychunk_rsp cc_rsp;
2212 : enum ndr_err_code ndr_ret;
2213 : NTSTATUS status;
2214 7 : bool ok = false;
2215 :
2216 7 : mem_ctx = talloc_new(tctx);
2217 7 : torture_assert_not_null_goto(tctx, mem_ctx, ok, done,
2218 : "talloc_new\n");
2219 :
2220 7 : ok = torture_smb2_tree_connect(tctx, tree->session, tctx, &tree2);
2221 7 : torture_assert_goto(tctx, ok == true, ok, done,
2222 : "torture_smb2_tree_connect failed\n");
2223 :
2224 7 : ok = test_setup_copy_chunk(tctx, tree, tree2, mem_ctx,
2225 : 2, /* 2 chunks */
2226 : FNAME,
2227 : &src_h, 4096, /* fill 4096 byte src file */
2228 : SEC_RIGHTS_FILE_ALL,
2229 : FNAME2,
2230 : &dest_h, 0, /* 0 byte dest file */
2231 : SEC_RIGHTS_FILE_ALL,
2232 : &cc_copy,
2233 : &ioctl);
2234 7 : torture_assert_goto(tctx, ok == true, ok, done,
2235 : "test_setup_copy_chunk failed\n");
2236 :
2237 6 : cc_copy.chunks[0].source_off = 0;
2238 6 : cc_copy.chunks[0].target_off = 0;
2239 6 : cc_copy.chunks[0].length = 4096;
2240 :
2241 : /* second chunk appends the same data to the first */
2242 6 : cc_copy.chunks[1].source_off = 0;
2243 6 : cc_copy.chunks[1].target_off = 4096;
2244 6 : cc_copy.chunks[1].length = 4096;
2245 :
2246 6 : ndr_ret = ndr_push_struct_blob(
2247 : &ioctl.smb2.in.out, mem_ctx, &cc_copy,
2248 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2249 6 : torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2250 : "ndr_push_srv_copychunk_copy\n");
2251 :
2252 6 : status = smb2_ioctl(tree2, mem_ctx, &ioctl.smb2);
2253 6 : torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "smb2_ioctl failed\n");
2254 :
2255 6 : ndr_ret = ndr_pull_struct_blob(
2256 : &ioctl.smb2.out.out, mem_ctx, &cc_rsp,
2257 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2258 :
2259 6 : torture_assert_ndr_success_goto(tctx, ndr_ret, ok, done,
2260 : "ndr_pull_srv_copychunk_rsp\n");
2261 :
2262 6 : ok = check_copy_chunk_rsp(tctx, &cc_rsp,
2263 : 2, /* chunks written */
2264 : 0, /* chunk bytes unsuccessfully written */
2265 : 8192); /* total bytes written */
2266 6 : torture_assert_goto(tctx, ok == true, ok, done,
2267 : "check_copy_chunk_rsp failed\n");
2268 :
2269 6 : ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 0, 4096, 0);
2270 6 : torture_assert_goto(tctx, ok == true, ok, done,
2271 : "check_pattern failed\n");
2272 :
2273 6 : ok = check_pattern(tctx, tree2, mem_ctx, dest_h, 4096, 4096, 0);
2274 6 : torture_assert_goto(tctx, ok == true, ok, done,
2275 : "check_pattern failed\n");
2276 :
2277 12 : done:
2278 7 : TALLOC_FREE(mem_ctx);
2279 7 : if (!smb2_util_handle_empty(src_h)) {
2280 7 : smb2_util_close(tree, src_h);
2281 : }
2282 7 : if (!smb2_util_handle_empty(dest_h)) {
2283 7 : smb2_util_close(tree2, dest_h);
2284 : }
2285 7 : smb2_util_unlink(tree, FNAME);
2286 7 : smb2_util_unlink(tree2, FNAME2);
2287 7 : if (tree2 != NULL) {
2288 7 : smb2_tdis(tree2);
2289 : }
2290 7 : return ok;
2291 : }
2292 :
2293 83 : static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
2294 : struct smb2_tree *tree,
2295 : TALLOC_CTX *mem_ctx,
2296 : struct smb2_handle *fh,
2297 : bool *compress_support)
2298 : {
2299 : NTSTATUS status;
2300 : union smb_fsinfo info;
2301 :
2302 83 : ZERO_STRUCT(info);
2303 83 : info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2304 83 : info.generic.handle = *fh;
2305 83 : status = smb2_getinfo_fs(tree, tree, &info);
2306 83 : if (!NT_STATUS_IS_OK(status)) {
2307 0 : return status;
2308 : }
2309 :
2310 83 : if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
2311 0 : *compress_support = true;
2312 : } else {
2313 83 : *compress_support = false;
2314 : }
2315 83 : return NT_STATUS_OK;
2316 : }
2317 :
2318 7 : static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
2319 : TALLOC_CTX *mem_ctx,
2320 : struct smb2_tree *tree,
2321 : struct smb2_handle fh,
2322 : uint16_t *_compression_fmt)
2323 : {
2324 : union smb_ioctl ioctl;
2325 : struct compression_state cmpr_state;
2326 : enum ndr_err_code ndr_ret;
2327 : NTSTATUS status;
2328 :
2329 7 : ZERO_STRUCT(ioctl);
2330 7 : ioctl.smb2.level = RAW_IOCTL_SMB2;
2331 7 : ioctl.smb2.in.file.handle = fh;
2332 7 : ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2333 7 : ioctl.smb2.in.max_output_response = sizeof(struct compression_state);
2334 7 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2335 :
2336 7 : status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2337 7 : if (!NT_STATUS_IS_OK(status)) {
2338 1 : return status;
2339 : }
2340 :
2341 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
2342 : &cmpr_state,
2343 : (ndr_pull_flags_fn_t)ndr_pull_compression_state);
2344 :
2345 6 : if (ndr_ret != NDR_ERR_SUCCESS) {
2346 0 : return NT_STATUS_INTERNAL_ERROR;
2347 : }
2348 :
2349 6 : *_compression_fmt = cmpr_state.format;
2350 6 : return NT_STATUS_OK;
2351 : }
2352 :
2353 13 : static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
2354 : TALLOC_CTX *mem_ctx,
2355 : struct smb2_tree *tree,
2356 : struct smb2_handle fh,
2357 : uint16_t compression_fmt)
2358 : {
2359 : union smb_ioctl ioctl;
2360 : struct compression_state cmpr_state;
2361 : enum ndr_err_code ndr_ret;
2362 : NTSTATUS status;
2363 :
2364 13 : ZERO_STRUCT(ioctl);
2365 13 : ioctl.smb2.level = RAW_IOCTL_SMB2;
2366 13 : ioctl.smb2.in.file.handle = fh;
2367 13 : ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
2368 13 : ioctl.smb2.in.max_output_response = 0;
2369 13 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2370 :
2371 13 : cmpr_state.format = compression_fmt;
2372 13 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
2373 : &cmpr_state,
2374 : (ndr_push_flags_fn_t)ndr_push_compression_state);
2375 13 : if (ndr_ret != NDR_ERR_SUCCESS) {
2376 0 : return NT_STATUS_INTERNAL_ERROR;
2377 : }
2378 :
2379 13 : status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2380 13 : return status;
2381 : }
2382 :
2383 7 : static bool test_ioctl_compress_file_flag(struct torture_context *torture,
2384 : struct smb2_tree *tree)
2385 : {
2386 : struct smb2_handle fh;
2387 : NTSTATUS status;
2388 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2389 : bool ok;
2390 : uint16_t compression_fmt;
2391 :
2392 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2393 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2394 : FILE_ATTRIBUTE_NORMAL);
2395 7 : torture_assert(torture, ok, "setup compression file");
2396 :
2397 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2398 : &ok);
2399 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2400 7 : if (!ok) {
2401 7 : smb2_util_close(tree, fh);
2402 7 : torture_skip(torture, "FS compression not supported\n");
2403 : }
2404 :
2405 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2406 : &compression_fmt);
2407 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2408 :
2409 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2410 : "initial compression state not NONE");
2411 :
2412 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2413 : COMPRESSION_FORMAT_DEFAULT);
2414 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2415 :
2416 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2417 : &compression_fmt);
2418 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2419 :
2420 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2421 : "invalid compression state after set");
2422 :
2423 0 : smb2_util_close(tree, fh);
2424 0 : talloc_free(tmp_ctx);
2425 0 : return true;
2426 : }
2427 :
2428 7 : static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
2429 : struct smb2_tree *tree)
2430 : {
2431 : struct smb2_handle dirh;
2432 : struct smb2_handle fh;
2433 : NTSTATUS status;
2434 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2435 : uint16_t compression_fmt;
2436 : bool ok;
2437 : char path_buf[PATH_MAX];
2438 :
2439 7 : smb2_deltree(tree, DNAME);
2440 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2441 : DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2442 : FILE_ATTRIBUTE_DIRECTORY);
2443 7 : torture_assert(torture, ok, "setup compression directory");
2444 :
2445 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2446 : &ok);
2447 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2448 7 : if (!ok) {
2449 7 : smb2_util_close(tree, dirh);
2450 7 : smb2_deltree(tree, DNAME);
2451 7 : torture_skip(torture, "FS compression not supported\n");
2452 : }
2453 :
2454 : /* set compression on parent dir, then check for inheritance */
2455 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2456 : COMPRESSION_FORMAT_LZNT1);
2457 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2458 :
2459 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2460 : &compression_fmt);
2461 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2462 :
2463 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2464 : "invalid compression state after set");
2465 :
2466 0 : snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2467 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2468 : path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
2469 : FILE_ATTRIBUTE_NORMAL);
2470 0 : torture_assert(torture, ok, "setup compression file");
2471 :
2472 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2473 : &compression_fmt);
2474 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2475 :
2476 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2477 : "compression attr not inherited by new file");
2478 :
2479 : /* check compressed data is consistent */
2480 0 : ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
2481 :
2482 : /* disable dir compression attr, file should remain compressed */
2483 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2484 : COMPRESSION_FORMAT_NONE);
2485 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2486 :
2487 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2488 : &compression_fmt);
2489 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2490 :
2491 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2492 : "file compression attr removed after dir change");
2493 0 : smb2_util_close(tree, fh);
2494 :
2495 : /* new files should no longer inherit compression attr */
2496 0 : snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2497 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2498 : path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2499 : FILE_ATTRIBUTE_NORMAL);
2500 0 : torture_assert(torture, ok, "setup file");
2501 :
2502 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2503 : &compression_fmt);
2504 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2505 :
2506 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2507 : "compression attr present on new file");
2508 :
2509 0 : smb2_util_close(tree, fh);
2510 0 : smb2_util_close(tree, dirh);
2511 0 : smb2_deltree(tree, DNAME);
2512 0 : talloc_free(tmp_ctx);
2513 0 : return true;
2514 : }
2515 :
2516 7 : static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
2517 : struct smb2_tree *tree)
2518 : {
2519 : struct smb2_handle fh;
2520 : NTSTATUS status;
2521 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2522 : bool ok;
2523 : uint16_t compression_fmt;
2524 :
2525 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2526 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2527 : FILE_ATTRIBUTE_NORMAL);
2528 7 : torture_assert(torture, ok, "setup compression file");
2529 :
2530 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2531 : &ok);
2532 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2533 7 : if (!ok) {
2534 7 : smb2_util_close(tree, fh);
2535 7 : torture_skip(torture, "FS compression not supported\n");
2536 : }
2537 :
2538 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2539 : 0x0042); /* bogus */
2540 0 : torture_assert_ntstatus_equal(torture, status,
2541 : NT_STATUS_INVALID_PARAMETER,
2542 : "invalid FSCTL_SET_COMPRESSION");
2543 :
2544 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2545 : &compression_fmt);
2546 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2547 :
2548 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2549 : "initial compression state not NONE");
2550 :
2551 0 : smb2_util_close(tree, fh);
2552 0 : talloc_free(tmp_ctx);
2553 0 : return true;
2554 : }
2555 :
2556 7 : static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2557 : struct smb2_tree *tree)
2558 : {
2559 : struct smb2_handle fh;
2560 : NTSTATUS status;
2561 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2562 : bool ok;
2563 : union smb_ioctl ioctl;
2564 :
2565 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2566 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2567 : FILE_ATTRIBUTE_NORMAL);
2568 7 : torture_assert(torture, ok, "setup compression file");
2569 :
2570 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2571 : &ok);
2572 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2573 7 : if (!ok) {
2574 7 : smb2_util_close(tree, fh);
2575 7 : torture_skip(torture, "FS compression not supported\n");
2576 : }
2577 :
2578 0 : ZERO_STRUCT(ioctl);
2579 0 : ioctl.smb2.level = RAW_IOCTL_SMB2;
2580 0 : ioctl.smb2.in.file.handle = fh;
2581 0 : ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2582 0 : ioctl.smb2.in.max_output_response = 0; /* no room for rsp data */
2583 0 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2584 :
2585 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2586 0 : if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2587 0 : && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2588 : /* neither Server 2k12 nor 2k8r2 response status */
2589 : torture_assert(torture, true,
2590 : "invalid FSCTL_SET_COMPRESSION");
2591 : }
2592 :
2593 0 : smb2_util_close(tree, fh);
2594 0 : talloc_free(tmp_ctx);
2595 0 : return true;
2596 : }
2597 :
2598 7 : static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2599 : struct smb2_tree *tree)
2600 : {
2601 : struct smb2_handle fh;
2602 : union smb_fileinfo io;
2603 : NTSTATUS status;
2604 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2605 : bool ok;
2606 :
2607 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2608 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2609 : FILE_ATTRIBUTE_NORMAL);
2610 7 : torture_assert(torture, ok, "setup compression file");
2611 :
2612 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2613 : &ok);
2614 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2615 7 : if (!ok) {
2616 7 : smb2_util_close(tree, fh);
2617 7 : torture_skip(torture, "FS compression not supported\n");
2618 : }
2619 :
2620 0 : ZERO_STRUCT(io);
2621 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2622 0 : io.generic.in.file.handle = fh;
2623 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2624 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2625 :
2626 0 : torture_assert(torture,
2627 : ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2628 : "compression attr before set");
2629 :
2630 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2631 : COMPRESSION_FORMAT_DEFAULT);
2632 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2633 :
2634 0 : ZERO_STRUCT(io);
2635 0 : io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2636 0 : io.generic.in.file.handle = fh;
2637 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2638 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2639 :
2640 0 : torture_assert(torture,
2641 : (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2642 : "no compression attr after set");
2643 :
2644 0 : smb2_util_close(tree, fh);
2645 0 : talloc_free(tmp_ctx);
2646 0 : return true;
2647 : }
2648 :
2649 : /*
2650 : * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2651 : * attribute.
2652 : */
2653 7 : static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2654 : struct smb2_tree *tree)
2655 : {
2656 : struct smb2_handle fh2;
2657 : union smb_fileinfo io;
2658 : NTSTATUS status;
2659 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2660 : uint16_t compression_fmt;
2661 : bool ok;
2662 :
2663 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2664 : FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2665 : (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2666 7 : torture_assert(torture, ok, "setup compression file");
2667 :
2668 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2669 : &ok);
2670 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2671 7 : if (!ok) {
2672 7 : smb2_util_close(tree, fh2);
2673 7 : torture_skip(torture, "FS compression not supported\n");
2674 : }
2675 :
2676 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2677 : &compression_fmt);
2678 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2679 :
2680 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2681 : "initial compression state not NONE");
2682 :
2683 0 : ZERO_STRUCT(io);
2684 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2685 0 : io.generic.in.file.handle = fh2;
2686 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2687 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2688 :
2689 0 : torture_assert(torture,
2690 : ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2691 : "incorrect compression attr");
2692 :
2693 0 : smb2_util_close(tree, fh2);
2694 0 : talloc_free(tmp_ctx);
2695 0 : return true;
2696 : }
2697 :
2698 7 : static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2699 : struct smb2_tree *tree)
2700 : {
2701 : struct smb2_handle fh;
2702 : struct smb2_handle dirh;
2703 : char path_buf[PATH_MAX];
2704 : NTSTATUS status;
2705 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2706 : bool ok;
2707 : uint16_t compression_fmt;
2708 :
2709 : struct smb2_create io;
2710 :
2711 7 : smb2_deltree(tree, DNAME);
2712 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2713 : DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2714 : FILE_ATTRIBUTE_DIRECTORY);
2715 7 : torture_assert(torture, ok, "setup compression directory");
2716 :
2717 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2718 : &ok);
2719 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2720 7 : if (!ok) {
2721 7 : smb2_util_close(tree, dirh);
2722 7 : smb2_deltree(tree, DNAME);
2723 7 : torture_skip(torture, "FS compression not supported\n");
2724 : }
2725 :
2726 : /* set compression on parent dir, then check for inheritance */
2727 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2728 : COMPRESSION_FORMAT_LZNT1);
2729 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2730 :
2731 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2732 : &compression_fmt);
2733 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2734 :
2735 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2736 : "invalid compression state after set");
2737 0 : smb2_util_close(tree, dirh);
2738 :
2739 0 : snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2740 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2741 : path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2742 : FILE_ATTRIBUTE_NORMAL);
2743 0 : torture_assert(torture, ok, "setup compression file");
2744 :
2745 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2746 : &compression_fmt);
2747 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2748 :
2749 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2750 : "compression attr not inherited by new file");
2751 0 : smb2_util_close(tree, fh);
2752 :
2753 0 : snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2754 :
2755 : /* NO_COMPRESSION option should block inheritance */
2756 0 : ZERO_STRUCT(io);
2757 0 : io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2758 0 : io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2759 0 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
2760 0 : io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2761 0 : io.in.share_access =
2762 : NTCREATEX_SHARE_ACCESS_DELETE|
2763 : NTCREATEX_SHARE_ACCESS_READ|
2764 : NTCREATEX_SHARE_ACCESS_WRITE;
2765 0 : io.in.fname = path_buf;
2766 :
2767 0 : status = smb2_create(tree, tmp_ctx, &io);
2768 0 : torture_assert_ntstatus_ok(torture, status, "file create");
2769 :
2770 0 : fh = io.out.file.handle;
2771 :
2772 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2773 : &compression_fmt);
2774 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2775 :
2776 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2777 : "compression attr inherited by NO_COMPRESSION file");
2778 0 : smb2_util_close(tree, fh);
2779 :
2780 :
2781 0 : snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2782 0 : ZERO_STRUCT(io);
2783 0 : io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2784 0 : io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2785 0 : io.in.create_disposition = NTCREATEX_DISP_CREATE;
2786 0 : io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2787 : | NTCREATEX_OPTIONS_DIRECTORY);
2788 0 : io.in.share_access =
2789 : NTCREATEX_SHARE_ACCESS_DELETE|
2790 : NTCREATEX_SHARE_ACCESS_READ|
2791 : NTCREATEX_SHARE_ACCESS_WRITE;
2792 0 : io.in.fname = path_buf;
2793 :
2794 0 : status = smb2_create(tree, tmp_ctx, &io);
2795 0 : torture_assert_ntstatus_ok(torture, status, "dir create");
2796 :
2797 0 : dirh = io.out.file.handle;
2798 :
2799 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2800 : &compression_fmt);
2801 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2802 :
2803 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2804 : "compression attr inherited by NO_COMPRESSION dir");
2805 0 : smb2_util_close(tree, dirh);
2806 0 : smb2_deltree(tree, DNAME);
2807 :
2808 0 : talloc_free(tmp_ctx);
2809 0 : return true;
2810 : }
2811 :
2812 : /* attempting to set compression via SetInfo should not stick */
2813 7 : static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2814 : struct smb2_tree *tree)
2815 : {
2816 : struct smb2_handle fh;
2817 : struct smb2_handle dirh;
2818 : union smb_fileinfo io;
2819 : union smb_setfileinfo set_io;
2820 : uint16_t compression_fmt;
2821 : NTSTATUS status;
2822 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2823 : bool ok;
2824 :
2825 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2826 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2827 : FILE_ATTRIBUTE_NORMAL);
2828 7 : torture_assert(torture, ok, "setup compression file");
2829 :
2830 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2831 : &ok);
2832 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2833 7 : if (!ok) {
2834 7 : smb2_util_close(tree, fh);
2835 7 : torture_skip(torture, "FS compression not supported\n");
2836 : }
2837 :
2838 0 : ZERO_STRUCT(io);
2839 0 : io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2840 0 : io.generic.in.file.handle = fh;
2841 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2842 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2843 :
2844 0 : torture_assert(torture,
2845 : ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2846 : "compression attr before set");
2847 :
2848 0 : ZERO_STRUCT(set_io);
2849 0 : set_io.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
2850 0 : set_io.basic_info.in.file.handle = fh;
2851 0 : set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2852 0 : set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2853 0 : set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2854 0 : set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2855 0 : set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2856 : | FILE_ATTRIBUTE_COMPRESSED);
2857 0 : status = smb2_setinfo_file(tree, &set_io);
2858 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2859 :
2860 0 : ZERO_STRUCT(io);
2861 0 : io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2862 0 : io.generic.in.file.handle = fh;
2863 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2864 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2865 :
2866 0 : torture_assert(torture,
2867 : ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2868 : "compression attr after set");
2869 :
2870 0 : smb2_util_close(tree, fh);
2871 0 : smb2_deltree(tree, DNAME);
2872 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2873 : DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2874 : FILE_ATTRIBUTE_DIRECTORY);
2875 0 : torture_assert(torture, ok, "setup compression directory");
2876 :
2877 0 : ZERO_STRUCT(io);
2878 0 : io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2879 0 : io.generic.in.file.handle = dirh;
2880 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2881 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2882 :
2883 0 : torture_assert(torture,
2884 : ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2885 : "compression attr before set");
2886 :
2887 0 : ZERO_STRUCT(set_io);
2888 0 : set_io.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
2889 0 : set_io.basic_info.in.file.handle = dirh;
2890 0 : set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2891 0 : set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2892 0 : set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2893 0 : set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2894 0 : set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2895 : | FILE_ATTRIBUTE_COMPRESSED);
2896 0 : status = smb2_setinfo_file(tree, &set_io);
2897 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2898 :
2899 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2900 : &compression_fmt);
2901 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2902 :
2903 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2904 : "dir compression set after SetInfo");
2905 :
2906 0 : smb2_util_close(tree, dirh);
2907 0 : talloc_free(tmp_ctx);
2908 0 : return true;
2909 : }
2910 :
2911 7 : static bool test_ioctl_compress_perms(struct torture_context *torture,
2912 : struct smb2_tree *tree)
2913 : {
2914 : struct smb2_handle fh;
2915 : uint16_t compression_fmt;
2916 : union smb_fileinfo io;
2917 : NTSTATUS status;
2918 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
2919 : bool ok;
2920 :
2921 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2922 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2923 : FILE_ATTRIBUTE_NORMAL);
2924 7 : torture_assert(torture, ok, "setup compression file");
2925 :
2926 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2927 : &ok);
2928 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2929 7 : smb2_util_close(tree, fh);
2930 7 : if (!ok) {
2931 7 : torture_skip(torture, "FS compression not supported\n");
2932 : }
2933 :
2934 : /* attempt get compression without READ_ATTR permission */
2935 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2936 : FNAME, &fh, 0,
2937 : (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2938 : | SEC_STD_READ_CONTROL
2939 : | SEC_FILE_READ_EA)),
2940 : FILE_ATTRIBUTE_NORMAL);
2941 0 : torture_assert(torture, ok, "setup compression file");
2942 :
2943 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2944 : &compression_fmt);
2945 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2946 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2947 : "compression set after create");
2948 0 : smb2_util_close(tree, fh);
2949 :
2950 : /* set compression without WRITE_ATTR permission should succeed */
2951 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2952 : FNAME, &fh, 0,
2953 : (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2954 : | SEC_STD_WRITE_DAC
2955 : | SEC_FILE_WRITE_EA)),
2956 : FILE_ATTRIBUTE_NORMAL);
2957 0 : torture_assert(torture, ok, "setup compression file");
2958 :
2959 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2960 : COMPRESSION_FORMAT_DEFAULT);
2961 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2962 0 : smb2_util_close(tree, fh);
2963 :
2964 0 : ok = test_setup_open(torture, tree, tmp_ctx,
2965 : FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2966 : FILE_ATTRIBUTE_NORMAL);
2967 0 : torture_assert(torture, ok, "setup compression file");
2968 0 : ZERO_STRUCT(io);
2969 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2970 0 : io.generic.in.file.handle = fh;
2971 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
2972 0 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2973 :
2974 0 : torture_assert(torture,
2975 : (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2976 : "incorrect compression attr");
2977 0 : smb2_util_close(tree, fh);
2978 :
2979 : /* attempt get compression without READ_DATA permission */
2980 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2981 : FNAME, &fh, 0,
2982 : (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2983 : FILE_ATTRIBUTE_NORMAL);
2984 0 : torture_assert(torture, ok, "setup compression file");
2985 :
2986 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2987 : &compression_fmt);
2988 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2989 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2990 : "compression enabled after set");
2991 0 : smb2_util_close(tree, fh);
2992 :
2993 : /* attempt get compression with only SYNCHRONIZE permission */
2994 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
2995 : FNAME, &fh, 0,
2996 : SEC_STD_SYNCHRONIZE,
2997 : FILE_ATTRIBUTE_NORMAL);
2998 0 : torture_assert(torture, ok, "setup compression file");
2999 :
3000 0 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
3001 : &compression_fmt);
3002 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
3003 0 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
3004 : "compression not enabled after set");
3005 0 : smb2_util_close(tree, fh);
3006 :
3007 : /* attempt to set compression without WRITE_DATA permission */
3008 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3009 : FNAME, &fh, 0,
3010 : (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
3011 : FILE_ATTRIBUTE_NORMAL);
3012 0 : torture_assert(torture, ok, "setup compression file");
3013 :
3014 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3015 : COMPRESSION_FORMAT_DEFAULT);
3016 0 : torture_assert_ntstatus_equal(torture, status,
3017 : NT_STATUS_ACCESS_DENIED,
3018 : "FSCTL_SET_COMPRESSION permission");
3019 0 : smb2_util_close(tree, fh);
3020 :
3021 0 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3022 : FNAME, &fh, 0,
3023 : (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
3024 : FILE_ATTRIBUTE_NORMAL);
3025 0 : torture_assert(torture, ok, "setup compression file");
3026 :
3027 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3028 : COMPRESSION_FORMAT_NONE);
3029 0 : torture_assert_ntstatus_equal(torture, status,
3030 : NT_STATUS_ACCESS_DENIED,
3031 : "FSCTL_SET_COMPRESSION permission");
3032 0 : smb2_util_close(tree, fh);
3033 :
3034 0 : talloc_free(tmp_ctx);
3035 0 : return true;
3036 : }
3037 :
3038 7 : static bool test_ioctl_compress_notsup_get(struct torture_context *torture,
3039 : struct smb2_tree *tree)
3040 : {
3041 : struct smb2_handle fh;
3042 : NTSTATUS status;
3043 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3044 : bool ok;
3045 : uint16_t compression_fmt;
3046 :
3047 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3048 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3049 : FILE_ATTRIBUTE_NORMAL);
3050 7 : torture_assert(torture, ok, "setup compression file");
3051 :
3052 : /* skip if the server DOES support compression */
3053 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3054 : &ok);
3055 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3056 7 : if (ok) {
3057 0 : smb2_util_close(tree, fh);
3058 0 : torture_skip(torture, "FS compression supported\n");
3059 : }
3060 :
3061 : /*
3062 : * Despite not supporting compression, we should get a successful
3063 : * response indicating that the file is uncompressed - like WS2016.
3064 : */
3065 7 : status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
3066 : &compression_fmt);
3067 7 : torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
3068 :
3069 6 : torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
3070 : "initial compression state not NONE");
3071 :
3072 6 : smb2_util_close(tree, fh);
3073 6 : talloc_free(tmp_ctx);
3074 6 : return true;
3075 : }
3076 :
3077 7 : static bool test_ioctl_compress_notsup_set(struct torture_context *torture,
3078 : struct smb2_tree *tree)
3079 : {
3080 : struct smb2_handle fh;
3081 : NTSTATUS status;
3082 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3083 : bool ok;
3084 :
3085 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3086 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3087 : FILE_ATTRIBUTE_NORMAL);
3088 7 : torture_assert(torture, ok, "setup compression file");
3089 :
3090 : /* skip if the server DOES support compression */
3091 7 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3092 : &ok);
3093 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3094 7 : if (ok) {
3095 0 : smb2_util_close(tree, fh);
3096 0 : torture_skip(torture, "FS compression supported\n");
3097 : }
3098 :
3099 7 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3100 : COMPRESSION_FORMAT_DEFAULT);
3101 7 : torture_assert_ntstatus_equal(torture, status,
3102 : NT_STATUS_NOT_SUPPORTED,
3103 : "FSCTL_SET_COMPRESSION default");
3104 :
3105 : /*
3106 : * Despite not supporting compression, we should get a successful
3107 : * response for set(COMPRESSION_FORMAT_NONE) - like WS2016 ReFS.
3108 : */
3109 6 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3110 : COMPRESSION_FORMAT_NONE);
3111 6 : torture_assert_ntstatus_ok(torture, status,
3112 : "FSCTL_SET_COMPRESSION none");
3113 :
3114 6 : smb2_util_close(tree, fh);
3115 6 : talloc_free(tmp_ctx);
3116 6 : return true;
3117 : }
3118 :
3119 : /*
3120 : basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
3121 : */
3122 7 : static bool test_ioctl_network_interface_info(struct torture_context *torture,
3123 : struct smb2_tree *tree)
3124 : {
3125 : union smb_ioctl ioctl;
3126 : struct smb2_handle fh;
3127 : NTSTATUS status;
3128 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3129 : struct fsctl_net_iface_info net_iface;
3130 : enum ndr_err_code ndr_ret;
3131 : uint32_t caps;
3132 :
3133 7 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3134 7 : if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
3135 1 : torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
3136 : }
3137 :
3138 6 : ZERO_STRUCT(ioctl);
3139 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3140 6 : fh.data[0] = UINT64_MAX;
3141 6 : fh.data[1] = UINT64_MAX;
3142 6 : ioctl.smb2.in.file.handle = fh;
3143 6 : ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
3144 6 : ioctl.smb2.in.max_output_response = 0x10000; /* Windows client sets this to 64KiB */
3145 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3146 :
3147 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3148 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
3149 :
3150 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
3151 : (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
3152 6 : torture_assert_ndr_success(torture, ndr_ret,
3153 : "ndr_pull_fsctl_net_iface_info");
3154 :
3155 6 : ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
3156 : "Network Interface Info", &net_iface);
3157 :
3158 6 : talloc_free(tmp_ctx);
3159 6 : return true;
3160 : }
3161 :
3162 : /*
3163 : * Check whether all @fs_support_flags are set in the server's
3164 : * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
3165 : */
3166 217 : static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
3167 : struct smb2_tree *tree,
3168 : TALLOC_CTX *mem_ctx,
3169 : struct smb2_handle *fh,
3170 : uint64_t fs_support_flags,
3171 : bool *supported)
3172 : {
3173 : NTSTATUS status;
3174 : union smb_fsinfo info;
3175 :
3176 217 : ZERO_STRUCT(info);
3177 217 : info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
3178 217 : info.generic.handle = *fh;
3179 217 : status = smb2_getinfo_fs(tree, tree, &info);
3180 217 : if (!NT_STATUS_IS_OK(status)) {
3181 0 : return status;
3182 : }
3183 :
3184 217 : if ((info.attribute_info.out.fs_attr & fs_support_flags)
3185 : == fs_support_flags) {
3186 102 : *supported = true;
3187 : } else {
3188 115 : *supported = false;
3189 : }
3190 217 : return NT_STATUS_OK;
3191 : }
3192 :
3193 140 : static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
3194 : TALLOC_CTX *mem_ctx,
3195 : struct smb2_tree *tree,
3196 : struct smb2_handle fh,
3197 : bool set)
3198 : {
3199 : union smb_ioctl ioctl;
3200 : NTSTATUS status;
3201 : uint8_t set_sparse;
3202 :
3203 140 : ZERO_STRUCT(ioctl);
3204 140 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3205 140 : ioctl.smb2.in.file.handle = fh;
3206 140 : ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3207 140 : ioctl.smb2.in.max_output_response = 0;
3208 140 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3209 140 : set_sparse = (set ? 0xFF : 0x0);
3210 140 : ioctl.smb2.in.out.data = &set_sparse;
3211 140 : ioctl.smb2.in.out.length = sizeof(set_sparse);
3212 :
3213 140 : status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
3214 140 : return status;
3215 : }
3216 :
3217 120 : static NTSTATUS test_sparse_get(struct torture_context *torture,
3218 : TALLOC_CTX *mem_ctx,
3219 : struct smb2_tree *tree,
3220 : struct smb2_handle fh,
3221 : bool *_is_sparse)
3222 : {
3223 : union smb_fileinfo io;
3224 : NTSTATUS status;
3225 :
3226 120 : ZERO_STRUCT(io);
3227 120 : io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
3228 120 : io.generic.in.file.handle = fh;
3229 120 : status = smb2_getinfo_file(tree, mem_ctx, &io);
3230 120 : if (!NT_STATUS_IS_OK(status)) {
3231 0 : return status;
3232 : }
3233 120 : *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
3234 :
3235 120 : return status;
3236 : }
3237 :
3238 : /*
3239 : * Manually test setting and clearing sparse flag. Intended for file system
3240 : * specifc tests to toggle the flag through SMB and check the status in the
3241 : * file system.
3242 : */
3243 2 : bool test_ioctl_set_sparse(struct torture_context *tctx)
3244 : {
3245 2 : bool set, ret = true;
3246 2 : const char *filename = NULL;
3247 2 : struct smb2_create create = { };
3248 2 : struct smb2_tree *tree = NULL;
3249 : NTSTATUS status;
3250 :
3251 2 : set = torture_setting_bool(tctx, "set_sparse", true);
3252 2 : filename = torture_setting_string(tctx, "filename", NULL);
3253 :
3254 2 : if (filename == NULL) {
3255 0 : torture_fail(tctx, "Need to provide filename through "
3256 : "--option=torture:filename=testfile\n");
3257 : return false;
3258 : }
3259 :
3260 2 : if (!torture_smb2_connection(tctx, &tree)) {
3261 0 : torture_comment(tctx, "Initializing smb2 connection failed.\n");
3262 0 : return false;
3263 : }
3264 :
3265 2 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
3266 2 : create.in.create_options = 0;
3267 2 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
3268 2 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
3269 : NTCREATEX_SHARE_ACCESS_WRITE |
3270 : NTCREATEX_SHARE_ACCESS_DELETE;
3271 2 : create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
3272 2 : create.in.fname = filename;
3273 :
3274 2 : status = smb2_create(tree, tctx, &create);
3275 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3276 : "CREATE failed.\n");
3277 :
3278 2 : status = test_ioctl_sparse_req(tctx, tctx, tree,
3279 : create.out.file.handle, set);
3280 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3281 : "FSCTL_SET_SPARSE failed.\n");
3282 2 : done:
3283 :
3284 2 : return ret;
3285 : }
3286 :
3287 7 : static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
3288 : struct smb2_tree *tree)
3289 : {
3290 : struct smb2_handle fh;
3291 : union smb_fileinfo io;
3292 : NTSTATUS status;
3293 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3294 : bool ok;
3295 : bool is_sparse;
3296 :
3297 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3298 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3299 : FILE_ATTRIBUTE_NORMAL);
3300 7 : torture_assert(torture, ok, "setup file");
3301 :
3302 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3303 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3304 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3305 7 : if (!ok) {
3306 1 : smb2_util_close(tree, fh);
3307 1 : torture_skip(torture, "Sparse files not supported\n");
3308 : }
3309 :
3310 6 : ZERO_STRUCT(io);
3311 6 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3312 6 : io.generic.in.file.handle = fh;
3313 6 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
3314 6 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
3315 :
3316 6 : torture_assert(torture,
3317 : ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
3318 : "sparse attr before set");
3319 :
3320 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3321 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3322 :
3323 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3324 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3325 6 : torture_assert(torture, is_sparse, "no sparse attr after set");
3326 :
3327 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3328 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3329 :
3330 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3331 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3332 6 : torture_assert(torture, !is_sparse, "sparse attr after unset");
3333 :
3334 6 : smb2_util_close(tree, fh);
3335 6 : talloc_free(tmp_ctx);
3336 6 : return true;
3337 : }
3338 :
3339 7 : static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
3340 : struct smb2_tree *tree)
3341 : {
3342 : struct smb2_handle fh;
3343 : NTSTATUS status;
3344 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3345 : bool ok;
3346 : bool is_sparse;
3347 :
3348 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3349 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3350 : (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
3351 7 : torture_assert(torture, ok, "setup file");
3352 :
3353 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3354 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3355 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3356 7 : if (!ok) {
3357 1 : smb2_util_close(tree, fh);
3358 1 : torture_skip(torture, "Sparse files not supported\n");
3359 : }
3360 :
3361 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3362 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3363 6 : torture_assert(torture, !is_sparse, "sparse attr on open");
3364 :
3365 6 : smb2_util_close(tree, fh);
3366 6 : talloc_free(tmp_ctx);
3367 6 : return true;
3368 : }
3369 :
3370 7 : static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
3371 : struct smb2_tree *tree)
3372 : {
3373 : struct smb2_handle dirh;
3374 : NTSTATUS status;
3375 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3376 : bool ok;
3377 :
3378 7 : smb2_deltree(tree, DNAME);
3379 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3380 : DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
3381 : FILE_ATTRIBUTE_DIRECTORY);
3382 7 : torture_assert(torture, ok, "setup sparse directory");
3383 :
3384 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
3385 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3386 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3387 7 : if (!ok) {
3388 1 : smb2_util_close(tree, dirh);
3389 1 : smb2_deltree(tree, DNAME);
3390 1 : torture_skip(torture, "Sparse files not supported\n");
3391 : }
3392 :
3393 : /* set sparse dir should fail, check for 2k12 & 2k8 response */
3394 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
3395 6 : torture_assert_ntstatus_equal(torture, status,
3396 : NT_STATUS_INVALID_PARAMETER,
3397 : "dir FSCTL_SET_SPARSE status");
3398 :
3399 6 : smb2_util_close(tree, dirh);
3400 6 : smb2_deltree(tree, DNAME);
3401 6 : talloc_free(tmp_ctx);
3402 6 : return true;
3403 : }
3404 :
3405 : /*
3406 : * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3407 : * buffer to indicate whether the flag should be set or cleared. When sent
3408 : * without a buffer, it must be handled as if SetSparse=TRUE.
3409 : */
3410 7 : static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
3411 : struct smb2_tree *tree)
3412 : {
3413 : struct smb2_handle fh;
3414 : union smb_ioctl ioctl;
3415 : NTSTATUS status;
3416 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3417 : bool ok;
3418 : bool is_sparse;
3419 :
3420 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3421 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3422 : FILE_ATTRIBUTE_NORMAL);
3423 7 : torture_assert(torture, ok, "setup file");
3424 :
3425 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3426 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3427 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3428 7 : if (!ok) {
3429 1 : smb2_util_close(tree, fh);
3430 1 : torture_skip(torture, "Sparse files not supported\n");
3431 : }
3432 :
3433 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3434 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3435 6 : torture_assert(torture, !is_sparse, "sparse attr before set");
3436 :
3437 6 : ZERO_STRUCT(ioctl);
3438 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3439 6 : ioctl.smb2.in.file.handle = fh;
3440 6 : ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3441 6 : ioctl.smb2.in.max_output_response = 0;
3442 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3443 : /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3444 :
3445 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3446 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3447 :
3448 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3449 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3450 6 : torture_assert(torture, is_sparse, "no sparse attr after set");
3451 :
3452 : /* second non-SetSparse request shouldn't toggle sparse */
3453 6 : ZERO_STRUCT(ioctl);
3454 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3455 6 : ioctl.smb2.in.file.handle = fh;
3456 6 : ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3457 6 : ioctl.smb2.in.max_output_response = 0;
3458 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3459 :
3460 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3461 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3462 :
3463 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3464 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3465 6 : torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
3466 :
3467 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3468 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3469 :
3470 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3471 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3472 6 : torture_assert(torture, !is_sparse, "sparse attr after unset");
3473 :
3474 6 : smb2_util_close(tree, fh);
3475 6 : talloc_free(tmp_ctx);
3476 6 : return true;
3477 : }
3478 :
3479 7 : static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
3480 : struct smb2_tree *tree)
3481 : {
3482 : struct smb2_handle fh;
3483 : union smb_ioctl ioctl;
3484 : NTSTATUS status;
3485 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3486 : bool ok;
3487 : bool is_sparse;
3488 : uint8_t buf[100];
3489 :
3490 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3491 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3492 : FILE_ATTRIBUTE_NORMAL);
3493 7 : torture_assert(torture, ok, "setup file");
3494 :
3495 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3496 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3497 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3498 7 : if (!ok) {
3499 1 : smb2_util_close(tree, fh);
3500 1 : torture_skip(torture, "Sparse files not supported\n");
3501 : }
3502 :
3503 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3504 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3505 6 : torture_assert(torture, !is_sparse, "sparse attr before set");
3506 :
3507 6 : ZERO_STRUCT(ioctl);
3508 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3509 6 : ioctl.smb2.in.file.handle = fh;
3510 6 : ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3511 6 : ioctl.smb2.in.max_output_response = 0;
3512 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3513 :
3514 : /*
3515 : * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3516 : * Windows still successfully processes the request.
3517 : */
3518 6 : ZERO_ARRAY(buf);
3519 6 : buf[0] = 0xFF; /* attempt to set sparse */
3520 6 : ioctl.smb2.in.out.data = buf;
3521 6 : ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3522 :
3523 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3524 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3525 :
3526 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3527 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3528 6 : torture_assert(torture, is_sparse, "no sparse attr after set");
3529 :
3530 6 : ZERO_STRUCT(ioctl);
3531 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3532 6 : ioctl.smb2.in.file.handle = fh;
3533 6 : ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3534 6 : ioctl.smb2.in.max_output_response = 0;
3535 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3536 :
3537 6 : ZERO_ARRAY(buf); /* clear sparse */
3538 6 : ioctl.smb2.in.out.data = buf;
3539 6 : ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3540 :
3541 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3542 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3543 :
3544 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3545 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3546 6 : torture_assert(torture, !is_sparse, "sparse attr after clear");
3547 :
3548 6 : smb2_util_close(tree, fh);
3549 6 : talloc_free(tmp_ctx);
3550 6 : return true;
3551 : }
3552 :
3553 222 : static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3554 : TALLOC_CTX *mem_ctx,
3555 : struct smb2_tree *tree,
3556 : struct smb2_handle fh,
3557 : int64_t req_off,
3558 : int64_t req_len,
3559 : struct file_alloced_range_buf **_rsp,
3560 : uint64_t *_rsp_count)
3561 : {
3562 : union smb_ioctl ioctl;
3563 : NTSTATUS status;
3564 : enum ndr_err_code ndr_ret;
3565 : struct file_alloced_range_buf far_buf;
3566 222 : struct file_alloced_range_buf *far_rsp = NULL;
3567 222 : uint64_t far_count = 0;
3568 : int i;
3569 222 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3570 222 : if (tmp_ctx == NULL) {
3571 0 : return NT_STATUS_NO_MEMORY;
3572 : }
3573 :
3574 222 : ZERO_STRUCT(ioctl);
3575 222 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3576 222 : ioctl.smb2.in.file.handle = fh;
3577 222 : ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3578 222 : ioctl.smb2.in.max_output_response = 1024;
3579 222 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3580 :
3581 222 : far_buf.file_off = req_off;
3582 222 : far_buf.len = req_len;
3583 :
3584 222 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3585 : &far_buf,
3586 : (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3587 222 : if (ndr_ret != NDR_ERR_SUCCESS) {
3588 0 : status = NT_STATUS_UNSUCCESSFUL;
3589 0 : goto err_out;
3590 : }
3591 :
3592 222 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3593 222 : if (!NT_STATUS_IS_OK(status)) {
3594 12 : goto err_out;
3595 : }
3596 :
3597 210 : if (ioctl.smb2.out.out.length == 0) {
3598 78 : goto done;
3599 : }
3600 :
3601 132 : if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3602 0 : torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3603 : ioctl.smb2.out.out.length);
3604 0 : status = NT_STATUS_INVALID_VIEW_SIZE;
3605 0 : goto err_out;
3606 : }
3607 :
3608 132 : far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3609 132 : far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3610 : far_count);
3611 132 : if (far_rsp == NULL) {
3612 0 : status = NT_STATUS_NO_MEMORY;
3613 0 : goto err_out;
3614 : }
3615 :
3616 318 : for (i = 0; i < far_count; i++) {
3617 186 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3618 186 : &far_rsp[i],
3619 : (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3620 186 : if (ndr_ret != NDR_ERR_SUCCESS) {
3621 0 : status = NT_STATUS_UNSUCCESSFUL;
3622 0 : goto err_out;
3623 : }
3624 : /* move to next buffer */
3625 186 : ioctl.smb2.out.out.data += sizeof(far_buf);
3626 186 : ioctl.smb2.out.out.length -= sizeof(far_buf);
3627 : }
3628 :
3629 132 : done:
3630 210 : *_rsp = far_rsp;
3631 210 : *_rsp_count = far_count;
3632 210 : status = NT_STATUS_OK;
3633 222 : err_out:
3634 222 : talloc_free(tmp_ctx);
3635 222 : return status;
3636 : }
3637 :
3638 7 : static bool test_ioctl_sparse_qar(struct torture_context *torture,
3639 : struct smb2_tree *tree)
3640 : {
3641 : struct smb2_handle fh;
3642 : NTSTATUS status;
3643 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3644 : bool ok;
3645 : bool is_sparse;
3646 7 : struct file_alloced_range_buf *far_rsp = NULL;
3647 7 : uint64_t far_count = 0;
3648 :
3649 : /* zero length file, shouldn't have any ranges */
3650 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3651 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3652 : FILE_ATTRIBUTE_NORMAL);
3653 7 : torture_assert(torture, ok, "setup file");
3654 :
3655 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3656 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3657 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3658 7 : if (!ok) {
3659 1 : smb2_util_close(tree, fh);
3660 1 : torture_skip(torture, "Sparse files not supported\n");
3661 : }
3662 :
3663 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3664 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3665 6 : torture_assert(torture, !is_sparse, "sparse attr before set");
3666 :
3667 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3668 : 0, /* off */
3669 : 0, /* len */
3670 : &far_rsp,
3671 : &far_count);
3672 6 : torture_assert_ntstatus_ok(torture, status,
3673 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3674 6 : torture_assert_u64_equal(torture, far_count, 0,
3675 : "unexpected response len");
3676 :
3677 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3678 : 0, /* off */
3679 : 1024, /* len */
3680 : &far_rsp,
3681 : &far_count);
3682 6 : torture_assert_ntstatus_ok(torture, status,
3683 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3684 6 : torture_assert_u64_equal(torture, far_count, 0,
3685 : "unexpected response len");
3686 :
3687 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3688 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3689 :
3690 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3691 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3692 6 : torture_assert(torture, is_sparse, "no sparse attr after set");
3693 :
3694 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3695 : 0, /* off */
3696 : 1024, /* len */
3697 : &far_rsp,
3698 : &far_count);
3699 6 : torture_assert_ntstatus_ok(torture, status,
3700 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3701 6 : torture_assert_u64_equal(torture, far_count, 0,
3702 : "unexpected response len");
3703 :
3704 : /* write into the (now) sparse file at 4k offset */
3705 6 : ok = write_pattern(torture, tree, tmp_ctx, fh,
3706 : 4096, /* off */
3707 : 1024, /* len */
3708 : 4096); /* pattern offset */
3709 6 : torture_assert(torture, ok, "write pattern");
3710 :
3711 : /*
3712 : * Query range before write off. Whether it's allocated or not is FS
3713 : * dependent. NTFS deallocates chunks in 64K increments, but others
3714 : * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3715 : */
3716 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3717 : 0, /* off */
3718 : 4096, /* len */
3719 : &far_rsp,
3720 : &far_count);
3721 6 : torture_assert_ntstatus_ok(torture, status,
3722 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3723 6 : if (far_count == 0) {
3724 6 : torture_comment(torture, "FS deallocated 4K chunk\n");
3725 : } else {
3726 : /* expect fully allocated */
3727 0 : torture_assert_u64_equal(torture, far_count, 1,
3728 : "unexpected response len");
3729 0 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3730 0 : torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3731 : }
3732 :
3733 : /*
3734 : * Query range before and past write, it should be allocated up to the
3735 : * end of the write.
3736 : */
3737 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3738 : 0, /* off */
3739 : 8192, /* len */
3740 : &far_rsp,
3741 : &far_count);
3742 6 : torture_assert_ntstatus_ok(torture, status,
3743 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3744 6 : torture_assert_u64_equal(torture, far_count, 1,
3745 : "unexpected response len");
3746 : /* FS dependent */
3747 6 : if (far_rsp[0].file_off == 4096) {
3748 : /* 4K chunk unallocated */
3749 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3750 6 : torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3751 : } else {
3752 : /* expect fully allocated */
3753 0 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3754 0 : torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3755 : }
3756 :
3757 6 : smb2_util_close(tree, fh);
3758 6 : talloc_free(tmp_ctx);
3759 6 : return true;
3760 : }
3761 :
3762 7 : static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3763 : struct smb2_tree *tree)
3764 : {
3765 : struct smb2_handle fh;
3766 : union smb_ioctl ioctl;
3767 : struct file_alloced_range_buf far_buf;
3768 : NTSTATUS status;
3769 : enum ndr_err_code ndr_ret;
3770 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3771 : bool ok;
3772 : size_t old_len;
3773 :
3774 : /* zero length file, shouldn't have any ranges */
3775 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3776 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3777 : FILE_ATTRIBUTE_NORMAL);
3778 7 : torture_assert(torture, ok, "setup file");
3779 :
3780 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3781 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3782 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3783 7 : if (!ok) {
3784 1 : smb2_util_close(tree, fh);
3785 1 : torture_skip(torture, "Sparse files not supported\n");
3786 : }
3787 :
3788 : /* no allocated ranges, no space for range response, should pass */
3789 6 : ZERO_STRUCT(ioctl);
3790 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3791 6 : ioctl.smb2.in.file.handle = fh;
3792 6 : ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3793 6 : ioctl.smb2.in.max_output_response = 0;
3794 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3795 :
3796 6 : far_buf.file_off = 0;
3797 6 : far_buf.len = 1024;
3798 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3799 : &far_buf,
3800 : (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3801 6 : torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3802 :
3803 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3804 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3805 :
3806 : /* write into the file at 4k offset */
3807 6 : ok = write_pattern(torture, tree, tmp_ctx, fh,
3808 : 0, /* off */
3809 : 1024, /* len */
3810 : 0); /* pattern offset */
3811 6 : torture_assert(torture, ok, "write pattern");
3812 :
3813 : /* allocated range, no space for range response, should fail */
3814 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3815 6 : torture_assert_ntstatus_equal(torture, status,
3816 : NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3817 :
3818 : /* oversize (2x) file_alloced_range_buf in request, should pass */
3819 6 : ioctl.smb2.in.max_output_response = 1024;
3820 6 : old_len = ioctl.smb2.in.out.length;
3821 7 : ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3822 6 : (ioctl.smb2.in.out.length * 2));
3823 6 : torture_assert(torture, ok, "2x data buffer");
3824 6 : memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3825 : old_len);
3826 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3827 6 : torture_assert_ntstatus_ok(torture, status, "qar too big");
3828 :
3829 : /* no file_alloced_range_buf in request, should fail */
3830 6 : data_blob_free(&ioctl.smb2.in.out);
3831 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3832 6 : torture_assert_ntstatus_equal(torture, status,
3833 : NT_STATUS_INVALID_PARAMETER, "qar empty");
3834 :
3835 6 : return true;
3836 : }
3837 :
3838 : /*
3839 : * 2.3.57 FSCTL_SET_ZERO_DATA Request
3840 : *
3841 : * How an implementation zeros data within a file is implementation-dependent.
3842 : * A file system MAY choose to deallocate regions of disk space that have been
3843 : * zeroed.<50>
3844 : * <50>
3845 : * ... NTFS might deallocate disk space in the file if the file is stored on an
3846 : * NTFS volume, and the file is sparse or compressed. It will free any allocated
3847 : * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3848 : * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3849 : * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3850 : * deallocated.
3851 : */
3852 194 : static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3853 : TALLOC_CTX *mem_ctx,
3854 : struct smb2_tree *tree,
3855 : struct smb2_handle fh,
3856 : int64_t off,
3857 : int64_t beyond_final_zero)
3858 : {
3859 : union smb_ioctl ioctl;
3860 : NTSTATUS status;
3861 : enum ndr_err_code ndr_ret;
3862 : struct file_zero_data_info zdata_info;
3863 194 : TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3864 194 : if (tmp_ctx == NULL) {
3865 0 : return NT_STATUS_NO_MEMORY;
3866 : }
3867 :
3868 194 : ZERO_STRUCT(ioctl);
3869 194 : ioctl.smb2.level = RAW_IOCTL_SMB2;
3870 194 : ioctl.smb2.in.file.handle = fh;
3871 194 : ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3872 194 : ioctl.smb2.in.max_output_response = 0;
3873 194 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3874 :
3875 194 : zdata_info.file_off = off;
3876 194 : zdata_info.beyond_final_zero = beyond_final_zero;
3877 :
3878 194 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3879 : &zdata_info,
3880 : (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3881 194 : if (ndr_ret != NDR_ERR_SUCCESS) {
3882 0 : status = NT_STATUS_UNSUCCESSFUL;
3883 0 : goto err_out;
3884 : }
3885 :
3886 194 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3887 194 : if (!NT_STATUS_IS_OK(status)) {
3888 36 : goto err_out;
3889 : }
3890 :
3891 158 : status = NT_STATUS_OK;
3892 194 : err_out:
3893 194 : talloc_free(tmp_ctx);
3894 194 : return status;
3895 : }
3896 :
3897 2 : bool test_ioctl_zero_data(struct torture_context *tctx)
3898 : {
3899 2 : bool ret = true;
3900 : int offset, beyond_final_zero;
3901 : const char *filename;
3902 : NTSTATUS status;
3903 2 : struct smb2_create create = { };
3904 2 : struct smb2_tree *tree = NULL;
3905 :
3906 2 : offset = torture_setting_int(tctx, "offset", -1);
3907 :
3908 2 : if (offset < 0) {
3909 0 : torture_fail(tctx, "Need to provide non-negative offset "
3910 : "through --option=torture:offset=NNN\n");
3911 : return false;
3912 : }
3913 :
3914 2 : beyond_final_zero = torture_setting_int(tctx, "beyond_final_zero",
3915 : -1);
3916 2 : if (beyond_final_zero < 0) {
3917 0 : torture_fail(tctx, "Need to provide non-negative "
3918 : "'beyond final zero' through "
3919 : "--option=torture:beyond_final_zero=NNN\n");
3920 : return false;
3921 : }
3922 2 : filename = torture_setting_string(tctx, "filename", NULL);
3923 2 : if (filename == NULL) {
3924 0 : torture_fail(tctx, "Need to provide filename through "
3925 : "--option=torture:filename=testfile\n");
3926 : return false;
3927 : }
3928 :
3929 2 : if (!torture_smb2_connection(tctx, &tree)) {
3930 0 : torture_comment(tctx, "Initializing smb2 connection failed.\n");
3931 0 : return false;
3932 : }
3933 :
3934 2 : create.in.desired_access = SEC_RIGHTS_DIR_ALL;
3935 2 : create.in.create_options = 0;
3936 2 : create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
3937 2 : create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
3938 : NTCREATEX_SHARE_ACCESS_WRITE |
3939 : NTCREATEX_SHARE_ACCESS_DELETE;
3940 2 : create.in.create_disposition = NTCREATEX_DISP_OPEN;
3941 2 : create.in.fname = filename;
3942 :
3943 2 : status = smb2_create(tree, tctx, &create);
3944 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3945 : "CREATE failed.\n");
3946 :
3947 2 : status = test_ioctl_zdata_req(tctx, tctx, tree,
3948 : create.out.file.handle,
3949 : offset,
3950 : beyond_final_zero);
3951 2 : torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3952 : "FSCTL_ZERO_DATA failed.\n");
3953 :
3954 2 : done:
3955 2 : return ret;
3956 : }
3957 :
3958 7 : static bool test_ioctl_sparse_punch(struct torture_context *torture,
3959 : struct smb2_tree *tree)
3960 : {
3961 : struct smb2_handle fh;
3962 : NTSTATUS status;
3963 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
3964 : bool ok;
3965 : bool is_sparse;
3966 7 : struct file_alloced_range_buf *far_rsp = NULL;
3967 7 : uint64_t far_count = 0;
3968 :
3969 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
3970 : FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3971 : FILE_ATTRIBUTE_NORMAL);
3972 7 : torture_assert(torture, ok, "setup file");
3973 :
3974 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3975 : FILE_SUPPORTS_SPARSE_FILES, &ok);
3976 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3977 7 : if (!ok) {
3978 1 : smb2_util_close(tree, fh);
3979 1 : torture_skip(torture, "Sparse files not supported\n");
3980 : }
3981 :
3982 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3983 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3984 6 : torture_assert(torture, !is_sparse, "sparse attr before set");
3985 :
3986 : /* zero (hole-punch) the data, without sparse flag */
3987 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3988 : 0, /* off */
3989 : 4096); /* beyond_final_zero */
3990 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
3991 :
3992 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3993 : 0, /* off */
3994 : 4096, /* len */
3995 : &far_rsp,
3996 : &far_count);
3997 6 : torture_assert_ntstatus_ok(torture, status,
3998 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3999 6 : torture_assert_u64_equal(torture, far_count, 1,
4000 : "unexpected response len");
4001 :
4002 : /* expect fully allocated */
4003 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4004 : "unexpected far off");
4005 6 : torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4006 : "unexpected far len");
4007 : /* check that the data is now zeroed */
4008 6 : ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4009 6 : torture_assert(torture, ok, "non-sparse zeroed range");
4010 :
4011 : /* set sparse */
4012 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4013 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4014 :
4015 : /* still fully allocated on NTFS, see note below for Samba */
4016 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4017 : 0, /* off */
4018 : 4096, /* len */
4019 : &far_rsp,
4020 : &far_count);
4021 6 : torture_assert_ntstatus_ok(torture, status,
4022 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4023 : /*
4024 : * FS specific: Samba uses PUNCH_HOLE to zero the range, and
4025 : * subsequently uses fallocate() to allocate the punched range if the
4026 : * file is marked non-sparse and "strict allocate" is enabled. In both
4027 : * cases, the zeroed range will not be detected by SEEK_DATA, so the
4028 : * range won't be present in QAR responses until the file is marked
4029 : * non-sparse again.
4030 : */
4031 6 : if (far_count == 0) {
4032 6 : torture_comment(torture, "non-sparse zeroed range disappeared "
4033 : "after marking sparse\n");
4034 : } else {
4035 : /* NTFS: range remains fully allocated */
4036 0 : torture_assert_u64_equal(torture, far_count, 1,
4037 : "unexpected response len");
4038 0 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4039 : "unexpected far off");
4040 0 : torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4041 : "unexpected far len");
4042 : }
4043 :
4044 : /* zero (hole-punch) the data, _with_ sparse flag */
4045 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4046 : 0, /* off */
4047 : 4096); /* beyond_final_zero */
4048 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4049 :
4050 : /* the range should no longer be alloced */
4051 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4052 : 0, /* off */
4053 : 4096, /* len */
4054 : &far_rsp,
4055 : &far_count);
4056 6 : torture_assert_ntstatus_ok(torture, status,
4057 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4058 6 : torture_assert_u64_equal(torture, far_count, 0,
4059 : "unexpected response len");
4060 :
4061 6 : ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4062 6 : torture_assert(torture, ok, "sparse zeroed range");
4063 :
4064 : /* remove sparse flag, this should "unsparse" the zeroed range */
4065 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4066 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4067 :
4068 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4069 : 0, /* off */
4070 : 4096, /* len */
4071 : &far_rsp,
4072 : &far_count);
4073 6 : torture_assert_ntstatus_ok(torture, status,
4074 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4075 6 : torture_assert_u64_equal(torture, far_count, 1,
4076 : "unexpected response len");
4077 : /* expect fully allocated */
4078 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4079 : "unexpected far off");
4080 6 : torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4081 : "unexpected far len");
4082 :
4083 6 : ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4084 6 : torture_assert(torture, ok, "sparse zeroed range");
4085 :
4086 6 : smb2_util_close(tree, fh);
4087 6 : talloc_free(tmp_ctx);
4088 6 : return true;
4089 : }
4090 :
4091 : /*
4092 : * Find the point at which a zeroed range in a sparse file is deallocated by the
4093 : * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
4094 : * increments. Also check whether zeroed neighbours are merged for deallocation.
4095 : */
4096 7 : static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
4097 : struct smb2_tree *tree)
4098 : {
4099 : struct smb2_handle fh;
4100 : NTSTATUS status;
4101 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
4102 : bool ok;
4103 : uint64_t file_size;
4104 : uint64_t hlen;
4105 7 : uint64_t dealloc_chunk_len = 0;
4106 7 : struct file_alloced_range_buf *far_rsp = NULL;
4107 7 : uint64_t far_count = 0;
4108 :
4109 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4110 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4111 : FILE_ATTRIBUTE_NORMAL);
4112 7 : torture_assert(torture, ok, "setup file 1");
4113 :
4114 : /* check for FS sparse file */
4115 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4116 : FILE_SUPPORTS_SPARSE_FILES, &ok);
4117 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4118 7 : if (!ok) {
4119 1 : smb2_util_close(tree, fh);
4120 1 : torture_skip(torture, "Sparse files not supported\n");
4121 : }
4122 :
4123 : /* set sparse */
4124 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4125 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4126 :
4127 6 : file_size = 1024 * 1024;
4128 :
4129 6 : ok = write_pattern(torture, tree, tmp_ctx, fh,
4130 : 0, /* off */
4131 : file_size, /* len */
4132 : 0); /* pattern offset */
4133 6 : torture_assert(torture, ok, "write pattern");
4134 :
4135 : /* check allocated ranges, should be fully allocated */
4136 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4137 : 0, /* off */
4138 : file_size, /* len */
4139 : &far_rsp,
4140 : &far_count);
4141 6 : torture_assert_ntstatus_ok(torture, status,
4142 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4143 6 : torture_assert_u64_equal(torture, far_count, 1,
4144 : "unexpected response len");
4145 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4146 : "unexpected far off");
4147 6 : torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4148 : "unexpected far len");
4149 :
4150 : /* punch holes in sizes of 1k increments */
4151 12 : for (hlen = 0; hlen <= file_size; hlen += 4096) {
4152 :
4153 : /* punch a hole from zero to the current increment */
4154 12 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4155 : 0, /* off */
4156 : hlen); /* beyond_final_zero */
4157 12 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4158 :
4159 : /* ensure hole is zeroed, and pattern is consistent */
4160 12 : ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
4161 12 : torture_assert(torture, ok, "sparse zeroed range");
4162 :
4163 12 : ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
4164 : file_size - hlen, hlen);
4165 12 : torture_assert(torture, ok, "allocated pattern range");
4166 :
4167 : /* Check allocated ranges, hole might have been deallocated */
4168 12 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4169 : 0, /* off */
4170 : file_size, /* len */
4171 : &far_rsp,
4172 : &far_count);
4173 12 : torture_assert_ntstatus_ok(torture, status,
4174 : "FSCTL_QUERY_ALLOCATED_RANGES");
4175 12 : if ((hlen == file_size) && (far_count == 0)) {
4176 : /* hole covered entire file, deallocation occurred */
4177 0 : dealloc_chunk_len = file_size;
4178 0 : break;
4179 : }
4180 :
4181 12 : torture_assert_u64_equal(torture, far_count, 1,
4182 : "unexpected response len");
4183 12 : if (far_rsp[0].file_off != 0) {
4184 : /*
4185 : * We now know the hole punch length needed to trigger a
4186 : * deallocation on this FS...
4187 : */
4188 6 : dealloc_chunk_len = hlen;
4189 6 : torture_comment(torture, "hole punch %ju@0 resulted in "
4190 : "deallocation of %ju@0\n",
4191 : (uintmax_t)hlen,
4192 6 : (uintmax_t)far_rsp[0].file_off);
4193 6 : torture_assert_u64_equal(torture,
4194 : file_size - far_rsp[0].len,
4195 : far_rsp[0].file_off,
4196 : "invalid alloced range");
4197 6 : break;
4198 : }
4199 : }
4200 :
4201 6 : if (dealloc_chunk_len == 0) {
4202 0 : torture_comment(torture, "strange, this FS never deallocates"
4203 : "zeroed ranges in sparse files\n");
4204 0 : return true; /* FS specific, not a failure */
4205 : }
4206 :
4207 : /*
4208 : * Check whether deallocation occurs when the (now known)
4209 : * deallocation chunk size is punched via two ZERO_DATA requests.
4210 : * I.e. Does the FS merge the two ranges and deallocate the chunk?
4211 : * NTFS on Windows Server 2012 does not.
4212 : */
4213 6 : ok = write_pattern(torture, tree, tmp_ctx, fh,
4214 : 0, /* off */
4215 : file_size, /* len */
4216 : 0); /* pattern offset */
4217 6 : torture_assert(torture, ok, "write pattern");
4218 :
4219 : /* divide dealloc chunk size by two, to use as punch length */
4220 6 : hlen = dealloc_chunk_len >> 1;
4221 :
4222 : /*
4223 : * /half of dealloc chunk size 1M\
4224 : * | |
4225 : * /offset 0 | /dealloc chunk size |
4226 : * |------------------ |-------------------|-------------------|
4227 : * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern |
4228 : */
4229 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4230 : 0, /* off */
4231 : hlen); /* beyond final zero */
4232 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4233 :
4234 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4235 : hlen, /* off */
4236 : dealloc_chunk_len); /* beyond final */
4237 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4238 :
4239 : /* ensure holes are zeroed, and pattern is consistent */
4240 6 : ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4241 6 : torture_assert(torture, ok, "sparse zeroed range");
4242 :
4243 6 : ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4244 : file_size - dealloc_chunk_len, dealloc_chunk_len);
4245 6 : torture_assert(torture, ok, "allocated pattern range");
4246 :
4247 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4248 : 0, /* off */
4249 : file_size, /* len */
4250 : &far_rsp,
4251 : &far_count);
4252 6 : torture_assert_ntstatus_ok(torture, status,
4253 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4254 :
4255 6 : if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
4256 0 : torture_comment(torture, "holes merged for deallocation of "
4257 : "full file\n");
4258 0 : return true;
4259 : }
4260 6 : torture_assert_u64_equal(torture, far_count, 1,
4261 : "unexpected response len");
4262 6 : if (far_rsp[0].file_off == dealloc_chunk_len) {
4263 0 : torture_comment(torture, "holes merged for deallocation of "
4264 : "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
4265 0 : torture_assert_u64_equal(torture,
4266 : file_size - far_rsp[0].len,
4267 : far_rsp[0].file_off,
4268 : "invalid alloced range");
4269 : } else {
4270 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4271 : "unexpected deallocation");
4272 6 : torture_comment(torture, "holes not merged for deallocation\n");
4273 : }
4274 :
4275 6 : smb2_util_close(tree, fh);
4276 :
4277 : /*
4278 : * Check whether an unwritten range is allocated when a sparse file is
4279 : * written to at an offset past the dealloc chunk size:
4280 : *
4281 : * /dealloc chunk size
4282 : * /offset 0 |
4283 : * |------------------ |-------------------|
4284 : * | unwritten | pattern |
4285 : */
4286 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4287 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4288 : FILE_ATTRIBUTE_NORMAL);
4289 6 : torture_assert(torture, ok, "setup file 1");
4290 :
4291 : /* set sparse */
4292 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4293 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4294 :
4295 6 : ok = write_pattern(torture, tree, tmp_ctx, fh,
4296 : dealloc_chunk_len, /* off */
4297 : 1024, /* len */
4298 : dealloc_chunk_len); /* pattern offset */
4299 6 : torture_assert(torture, ok, "write pattern");
4300 :
4301 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4302 : 0, /* off */
4303 6 : dealloc_chunk_len + 1024, /* len */
4304 : &far_rsp,
4305 : &far_count);
4306 6 : torture_assert_ntstatus_ok(torture, status,
4307 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4308 6 : torture_assert_u64_equal(torture, far_count, 1,
4309 : "unexpected response len");
4310 6 : if (far_rsp[0].file_off == 0) {
4311 0 : torture_assert_u64_equal(torture, far_rsp[0].len,
4312 : dealloc_chunk_len + 1024,
4313 : "unexpected far len");
4314 0 : torture_comment(torture, "unwritten range fully allocated\n");
4315 : } else {
4316 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4317 : "unexpected deallocation");
4318 6 : torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4319 : "unexpected far len");
4320 6 : torture_comment(torture, "unwritten range not allocated\n");
4321 : }
4322 :
4323 6 : ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
4324 6 : torture_assert(torture, ok, "sparse zeroed range");
4325 :
4326 6 : ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
4327 : 1024, dealloc_chunk_len);
4328 6 : torture_assert(torture, ok, "allocated pattern range");
4329 :
4330 : /* unsparse, should now be fully allocated */
4331 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4332 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4333 :
4334 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4335 : 0, /* off */
4336 6 : dealloc_chunk_len + 1024, /* len */
4337 : &far_rsp,
4338 : &far_count);
4339 6 : torture_assert_ntstatus_ok(torture, status,
4340 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4341 6 : torture_assert_u64_equal(torture, far_count, 1,
4342 : "unexpected response len");
4343 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4344 : "unexpected deallocation");
4345 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
4346 : dealloc_chunk_len + 1024,
4347 : "unexpected far len");
4348 :
4349 6 : smb2_util_close(tree, fh);
4350 6 : talloc_free(tmp_ctx);
4351 6 : return true;
4352 : }
4353 :
4354 : /* check whether a file with compression and sparse attrs can be deallocated */
4355 7 : static bool test_ioctl_sparse_compressed(struct torture_context *torture,
4356 : struct smb2_tree *tree)
4357 : {
4358 : struct smb2_handle fh;
4359 : NTSTATUS status;
4360 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
4361 : bool ok;
4362 7 : uint64_t file_size = 1024 * 1024;
4363 7 : struct file_alloced_range_buf *far_rsp = NULL;
4364 7 : uint64_t far_count = 0;
4365 :
4366 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4367 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4368 : FILE_ATTRIBUTE_NORMAL);
4369 7 : torture_assert(torture, ok, "setup file 1");
4370 :
4371 : /* check for FS sparse file and compression support */
4372 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4373 : FILE_SUPPORTS_SPARSE_FILES, &ok);
4374 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4375 7 : if (!ok) {
4376 1 : smb2_util_close(tree, fh);
4377 1 : torture_skip(torture, "Sparse files not supported\n");
4378 : }
4379 :
4380 6 : status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
4381 : &ok);
4382 6 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4383 6 : if (!ok) {
4384 6 : smb2_util_close(tree, fh);
4385 6 : torture_skip(torture, "FS compression not supported\n");
4386 : }
4387 :
4388 : /* set compression and write some data */
4389 0 : status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
4390 : COMPRESSION_FORMAT_DEFAULT);
4391 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
4392 :
4393 0 : ok = write_pattern(torture, tree, tmp_ctx, fh,
4394 : 0, /* off */
4395 : file_size, /* len */
4396 : 0); /* pattern offset */
4397 0 : torture_assert(torture, ok, "write pattern");
4398 :
4399 : /* set sparse - now sparse and compressed */
4400 0 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4401 0 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4402 :
4403 : /* check allocated ranges, should be fully alloced */
4404 0 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4405 : 0, /* off */
4406 : file_size, /* len */
4407 : &far_rsp,
4408 : &far_count);
4409 0 : torture_assert_ntstatus_ok(torture, status,
4410 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4411 0 : torture_assert_u64_equal(torture, far_count, 1,
4412 : "unexpected response len");
4413 0 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4414 : "unexpected far off");
4415 0 : torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4416 : "unexpected far len");
4417 :
4418 : /* zero (hole-punch) all data, with sparse and compressed attrs */
4419 0 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4420 : 0, /* off */
4421 : file_size); /* beyond_final_zero */
4422 0 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4423 :
4424 : /*
4425 : * Windows Server 2012 still deallocates a zeroed range when a sparse
4426 : * file carries the compression attribute.
4427 : */
4428 0 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4429 : 0, /* off */
4430 : file_size, /* len */
4431 : &far_rsp,
4432 : &far_count);
4433 0 : torture_assert_ntstatus_ok(torture, status,
4434 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4435 0 : if (far_count == 0) {
4436 0 : torture_comment(torture, "sparse & compressed file "
4437 : "deallocated after hole-punch\n");
4438 : } else {
4439 0 : torture_assert_u64_equal(torture, far_count, 1,
4440 : "unexpected response len");
4441 0 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4442 : "unexpected far off");
4443 0 : torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4444 : "unexpected far len");
4445 0 : torture_comment(torture, "sparse & compressed file fully "
4446 : "allocated after hole-punch\n");
4447 : }
4448 :
4449 0 : smb2_util_close(tree, fh);
4450 0 : talloc_free(tmp_ctx);
4451 0 : return true;
4452 : }
4453 :
4454 : /*
4455 : * Create a sparse file, then attempt to copy unallocated and allocated ranges
4456 : * into a target file using FSCTL_SRV_COPYCHUNK.
4457 : */
4458 7 : static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
4459 : struct smb2_tree *tree)
4460 : {
4461 : struct smb2_handle src_h;
4462 : struct smb2_handle dest_h;
4463 : NTSTATUS status;
4464 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
4465 : bool ok;
4466 7 : uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4467 7 : struct file_alloced_range_buf *far_rsp = NULL;
4468 7 : uint64_t far_count = 0;
4469 : union smb_ioctl ioctl;
4470 : struct srv_copychunk_copy cc_copy;
4471 : struct srv_copychunk_rsp cc_rsp;
4472 : enum ndr_err_code ndr_ret;
4473 :
4474 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4475 : FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
4476 : FILE_ATTRIBUTE_NORMAL);
4477 7 : torture_assert(torture, ok, "setup file");
4478 :
4479 : /* check for FS sparse file support */
4480 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
4481 : FILE_SUPPORTS_SPARSE_FILES, &ok);
4482 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4483 7 : smb2_util_close(tree, src_h);
4484 7 : if (!ok) {
4485 1 : torture_skip(torture, "Sparse files not supported\n");
4486 : }
4487 :
4488 6 : ok = test_setup_copy_chunk(torture, tree, tree, tmp_ctx,
4489 : 1, /* chunks */
4490 : FNAME,
4491 : &src_h, 0, /* src file */
4492 : SEC_RIGHTS_FILE_ALL,
4493 : FNAME2,
4494 : &dest_h, 0, /* dest file */
4495 : SEC_RIGHTS_FILE_ALL,
4496 : &cc_copy,
4497 : &ioctl);
4498 6 : torture_assert(torture, ok, "setup copy chunk error");
4499 :
4500 : /* set sparse */
4501 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
4502 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4503 :
4504 : /* start after dealloc_chunk_len, to create an unwritten sparse range */
4505 6 : ok = write_pattern(torture, tree, tmp_ctx, src_h,
4506 : dealloc_chunk_len, /* off */
4507 : 1024, /* len */
4508 : dealloc_chunk_len); /* pattern offset */
4509 6 : torture_assert(torture, ok, "write pattern");
4510 :
4511 : /* Skip test if 64k chunk is allocated - FS specific */
4512 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
4513 : 0, /* off */
4514 6 : dealloc_chunk_len + 1024, /* len */
4515 : &far_rsp,
4516 : &far_count);
4517 6 : torture_assert_ntstatus_ok(torture, status,
4518 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4519 6 : torture_assert_u64_equal(torture, far_count, 1,
4520 : "unexpected response len");
4521 6 : if (far_rsp[0].file_off == 0) {
4522 0 : torture_skip(torture, "unwritten range fully allocated\n");
4523 : }
4524 :
4525 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
4526 : "unexpected allocation");
4527 6 : torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4528 : "unexpected far len");
4529 :
4530 : /* copy-chunk unallocated + written ranges into non-sparse dest */
4531 :
4532 6 : cc_copy.chunks[0].source_off = 0;
4533 6 : cc_copy.chunks[0].target_off = 0;
4534 6 : cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
4535 :
4536 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4537 : &cc_copy,
4538 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4539 6 : torture_assert_ndr_success(torture, ndr_ret,
4540 : "ndr_push_srv_copychunk_copy");
4541 :
4542 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4543 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4544 :
4545 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4546 : &cc_rsp,
4547 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4548 6 : torture_assert_ndr_success(torture, ndr_ret,
4549 : "ndr_pull_srv_copychunk_rsp");
4550 :
4551 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
4552 : 1, /* chunks written */
4553 : 0, /* chunk bytes unsuccessfully written */
4554 : dealloc_chunk_len + 1024); /* bytes written */
4555 6 : torture_assert(torture, ok, "bad copy chunk response data");
4556 :
4557 6 : ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4558 6 : torture_assert(torture, ok, "sparse zeroed range");
4559 :
4560 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4561 : 1024, dealloc_chunk_len);
4562 6 : torture_assert(torture, ok, "copychunked range");
4563 :
4564 : /* copied range should be allocated in non-sparse dest */
4565 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4566 : 0, /* off */
4567 6 : dealloc_chunk_len + 1024, /* len */
4568 : &far_rsp,
4569 : &far_count);
4570 6 : torture_assert_ntstatus_ok(torture, status,
4571 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4572 6 : torture_assert_u64_equal(torture, far_count, 1,
4573 : "unexpected response len");
4574 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4575 : "unexpected allocation");
4576 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
4577 : dealloc_chunk_len + 1024,
4578 : "unexpected far len");
4579 :
4580 : /* set dest as sparse */
4581 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
4582 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4583 :
4584 : /* zero (hole-punch) all data */
4585 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
4586 : 0, /* off */
4587 6 : dealloc_chunk_len + 1024);
4588 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4589 :
4590 : /* zeroed range might be deallocated */
4591 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4592 : 0, /* off */
4593 6 : dealloc_chunk_len + 1024, /* len */
4594 : &far_rsp,
4595 : &far_count);
4596 6 : torture_assert_ntstatus_ok(torture, status,
4597 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4598 6 : if (far_count == 0) {
4599 : /* FS specific (e.g. NTFS) */
4600 0 : torture_comment(torture, "FS deallocates file on full-range "
4601 : "punch\n");
4602 : } else {
4603 : /* FS specific (e.g. EXT4) */
4604 6 : torture_comment(torture, "FS doesn't deallocate file on "
4605 : "full-range punch\n");
4606 : }
4607 6 : ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
4608 : dealloc_chunk_len + 1024);
4609 6 : torture_assert(torture, ok, "punched zeroed range");
4610 :
4611 : /* copy-chunk again, this time with sparse dest */
4612 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4613 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
4614 :
4615 6 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4616 : &cc_rsp,
4617 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4618 6 : torture_assert_ndr_success(torture, ndr_ret,
4619 : "ndr_pull_srv_copychunk_rsp");
4620 :
4621 6 : ok = check_copy_chunk_rsp(torture, &cc_rsp,
4622 : 1, /* chunks written */
4623 : 0, /* chunk bytes unsuccessfully written */
4624 : dealloc_chunk_len + 1024); /* bytes written */
4625 6 : torture_assert(torture, ok, "bad copy chunk response data");
4626 :
4627 6 : ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
4628 6 : torture_assert(torture, ok, "sparse zeroed range");
4629 :
4630 6 : ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
4631 : 1024, dealloc_chunk_len);
4632 6 : torture_assert(torture, ok, "copychunked range");
4633 :
4634 : /* copied range may be allocated in sparse dest */
4635 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
4636 : 0, /* off */
4637 6 : dealloc_chunk_len + 1024, /* len */
4638 : &far_rsp,
4639 : &far_count);
4640 6 : torture_assert_ntstatus_ok(torture, status,
4641 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4642 6 : torture_assert_u64_equal(torture, far_count, 1,
4643 : "unexpected response len");
4644 : /*
4645 : * FS specific: sparse region may be unallocated in dest if copy-chunk
4646 : * is handled in a sparse preserving way - E.g. vfs_btrfs
4647 : * with BTRFS_IOC_CLONE_RANGE.
4648 : */
4649 6 : if (far_rsp[0].file_off == dealloc_chunk_len) {
4650 0 : torture_comment(torture, "copy-chunk sparse range preserved\n");
4651 0 : torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
4652 : "unexpected far len");
4653 : } else {
4654 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4655 : "unexpected allocation");
4656 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
4657 : dealloc_chunk_len + 1024,
4658 : "unexpected far len");
4659 : }
4660 :
4661 6 : smb2_util_close(tree, src_h);
4662 6 : smb2_util_close(tree, dest_h);
4663 6 : talloc_free(tmp_ctx);
4664 6 : return true;
4665 : }
4666 :
4667 7 : static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
4668 : struct smb2_tree *tree)
4669 : {
4670 : struct smb2_handle fh;
4671 : NTSTATUS status;
4672 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
4673 : bool ok;
4674 : bool is_sparse;
4675 : int i;
4676 :
4677 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4678 : FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
4679 : FILE_ATTRIBUTE_NORMAL);
4680 7 : torture_assert(torture, ok, "setup file");
4681 :
4682 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4683 : FILE_SUPPORTS_SPARSE_FILES, &ok);
4684 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4685 7 : if (!ok) {
4686 1 : smb2_util_close(tree, fh);
4687 1 : torture_skip(torture, "Sparse files not supported\n");
4688 : }
4689 :
4690 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4691 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4692 6 : torture_assert(torture, !is_sparse, "sparse attr before set");
4693 :
4694 : /* loop twice, without and with sparse attrib */
4695 18 : for (i = 0; i <= 1; i++) {
4696 : union smb_fileinfo io;
4697 12 : struct file_alloced_range_buf *far_rsp = NULL;
4698 12 : uint64_t far_count = 0;
4699 :
4700 : /* get size before & after. zero data should never change it */
4701 12 : ZERO_STRUCT(io);
4702 12 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4703 12 : io.generic.in.file.handle = fh;
4704 12 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
4705 12 : torture_assert_ntstatus_ok(torture, status, "getinfo");
4706 12 : torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4707 : 4096, "size after IO");
4708 :
4709 : /* valid 8 byte zero data, but after EOF */
4710 12 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4711 : 4096, /* off */
4712 : 4104); /* beyond_final_zero */
4713 12 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4714 :
4715 : /* valid 8 byte zero data, but after EOF */
4716 12 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4717 : 8192, /* off */
4718 : 8200); /* beyond_final_zero */
4719 12 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4720 :
4721 12 : ZERO_STRUCT(io);
4722 12 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4723 12 : io.generic.in.file.handle = fh;
4724 12 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
4725 12 : torture_assert_ntstatus_ok(torture, status, "getinfo");
4726 12 : torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4727 : 4096, "size after IO");
4728 :
4729 : /* valid 0 byte zero data, without sparse flag */
4730 12 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4731 : 4095, /* off */
4732 : 4095); /* beyond_final_zero */
4733 12 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4734 :
4735 : /* INVALID off is past beyond_final_zero */
4736 12 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4737 : 4096, /* off */
4738 : 4095); /* beyond_final_zero */
4739 12 : torture_assert_ntstatus_equal(torture, status,
4740 : NT_STATUS_INVALID_PARAMETER,
4741 : "invalid zero_data");
4742 :
4743 : /* zero length QAR - valid */
4744 12 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4745 : 0, /* off */
4746 : 0, /* len */
4747 : &far_rsp, &far_count);
4748 12 : torture_assert_ntstatus_ok(torture, status,
4749 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4750 12 : torture_assert_u64_equal(torture, far_count, 0,
4751 : "unexpected response len");
4752 :
4753 : /* QAR after EOF - valid */
4754 12 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4755 : 4096, /* off */
4756 : 1024, /* len */
4757 : &far_rsp, &far_count);
4758 12 : torture_assert_ntstatus_ok(torture, status,
4759 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4760 12 : torture_assert_u64_equal(torture, far_count, 0,
4761 : "unexpected response len");
4762 :
4763 : /* set sparse */
4764 12 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4765 : true);
4766 12 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4767 : }
4768 :
4769 6 : smb2_util_close(tree, fh);
4770 6 : talloc_free(tmp_ctx);
4771 6 : return true;
4772 : }
4773 :
4774 7 : static bool test_ioctl_sparse_perms(struct torture_context *torture,
4775 : struct smb2_tree *tree)
4776 : {
4777 : struct smb2_handle fh;
4778 : NTSTATUS status;
4779 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
4780 : bool ok;
4781 : bool is_sparse;
4782 7 : struct file_alloced_range_buf *far_rsp = NULL;
4783 7 : uint64_t far_count = 0;
4784 :
4785 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4786 : FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4787 : FILE_ATTRIBUTE_NORMAL);
4788 7 : torture_assert(torture, ok, "setup file");
4789 :
4790 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4791 : FILE_SUPPORTS_SPARSE_FILES, &ok);
4792 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4793 7 : smb2_util_close(tree, fh);
4794 7 : if (!ok) {
4795 1 : torture_skip(torture, "Sparse files not supported\n");
4796 : }
4797 :
4798 : /* set sparse without WRITE_ATTR permission should succeed */
4799 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4800 : FNAME, &fh, 0,
4801 : (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4802 : | SEC_STD_WRITE_DAC
4803 : | SEC_FILE_WRITE_EA)),
4804 : FILE_ATTRIBUTE_NORMAL);
4805 6 : torture_assert(torture, ok, "setup file");
4806 :
4807 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4808 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4809 6 : smb2_util_close(tree, fh);
4810 :
4811 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4812 : FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4813 : FILE_ATTRIBUTE_NORMAL);
4814 6 : torture_assert(torture, ok, "setup file");
4815 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4816 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4817 6 : torture_assert(torture, is_sparse, "sparse after set");
4818 6 : smb2_util_close(tree, fh);
4819 :
4820 : /* attempt get sparse without READ_DATA permission */
4821 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4822 : FNAME, &fh, 0,
4823 : (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4824 : FILE_ATTRIBUTE_NORMAL);
4825 6 : torture_assert(torture, ok, "setup file");
4826 :
4827 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4828 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4829 6 : torture_assert(torture, !is_sparse, "sparse set");
4830 6 : smb2_util_close(tree, fh);
4831 :
4832 : /* attempt to set sparse with only WRITE_ATTR permission */
4833 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4834 : FNAME, &fh, 0,
4835 : SEC_FILE_WRITE_ATTRIBUTE,
4836 : FILE_ATTRIBUTE_NORMAL);
4837 6 : torture_assert(torture, ok, "setup file");
4838 :
4839 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4840 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4841 6 : smb2_util_close(tree, fh);
4842 :
4843 : /* attempt to set sparse with only WRITE_DATA permission */
4844 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4845 : FNAME, &fh, 0,
4846 : SEC_FILE_WRITE_DATA,
4847 : FILE_ATTRIBUTE_NORMAL);
4848 6 : torture_assert(torture, ok, "setup file");
4849 :
4850 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4851 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4852 6 : smb2_util_close(tree, fh);
4853 :
4854 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4855 : FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4856 : FILE_ATTRIBUTE_NORMAL);
4857 6 : torture_assert(torture, ok, "setup file");
4858 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4859 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4860 6 : torture_assert(torture, is_sparse, "sparse after set");
4861 6 : smb2_util_close(tree, fh);
4862 :
4863 : /* attempt to set sparse with only APPEND_DATA permission */
4864 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4865 : FNAME, &fh, 0,
4866 : SEC_FILE_APPEND_DATA,
4867 : FILE_ATTRIBUTE_NORMAL);
4868 6 : torture_assert(torture, ok, "setup file");
4869 :
4870 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4871 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4872 6 : smb2_util_close(tree, fh);
4873 :
4874 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4875 : FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4876 : FILE_ATTRIBUTE_NORMAL);
4877 6 : torture_assert(torture, ok, "setup file");
4878 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4879 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4880 6 : torture_assert(torture, is_sparse, "sparse after set");
4881 6 : smb2_util_close(tree, fh);
4882 :
4883 : /* attempt to set sparse with only WRITE_EA permission - should fail */
4884 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4885 : FNAME, &fh, 0,
4886 : SEC_FILE_WRITE_EA,
4887 : FILE_ATTRIBUTE_NORMAL);
4888 6 : torture_assert(torture, ok, "setup file");
4889 :
4890 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4891 6 : torture_assert_ntstatus_equal(torture, status,
4892 : NT_STATUS_ACCESS_DENIED,
4893 : "FSCTL_SET_SPARSE permission");
4894 6 : smb2_util_close(tree, fh);
4895 :
4896 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4897 : FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4898 : FILE_ATTRIBUTE_NORMAL);
4899 6 : torture_assert(torture, ok, "setup file");
4900 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4901 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4902 6 : torture_assert(torture, !is_sparse, "sparse after set");
4903 6 : smb2_util_close(tree, fh);
4904 :
4905 : /* attempt QAR with only READ_ATTR permission - should fail */
4906 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4907 : FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4908 : FILE_ATTRIBUTE_NORMAL);
4909 6 : torture_assert(torture, ok, "setup file");
4910 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4911 : 4096, /* off */
4912 : 1024, /* len */
4913 : &far_rsp, &far_count);
4914 6 : torture_assert_ntstatus_equal(torture, status,
4915 : NT_STATUS_ACCESS_DENIED,
4916 : "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4917 6 : smb2_util_close(tree, fh);
4918 :
4919 : /* attempt QAR with only READ_DATA permission */
4920 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4921 : FNAME, &fh, SEC_FILE_READ_DATA,
4922 : FILE_ATTRIBUTE_NORMAL);
4923 6 : torture_assert(torture, ok, "setup file");
4924 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4925 : 0, /* off */
4926 : 1024, /* len */
4927 : &far_rsp, &far_count);
4928 6 : torture_assert_ntstatus_ok(torture, status,
4929 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4930 6 : torture_assert_u64_equal(torture, far_count, 0,
4931 : "unexpected response len");
4932 6 : smb2_util_close(tree, fh);
4933 :
4934 : /* attempt QAR with only READ_EA permission - should fail */
4935 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4936 : FNAME, &fh, SEC_FILE_READ_EA,
4937 : FILE_ATTRIBUTE_NORMAL);
4938 6 : torture_assert(torture, ok, "setup file");
4939 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4940 : 4096, /* off */
4941 : 1024, /* len */
4942 : &far_rsp, &far_count);
4943 6 : torture_assert_ntstatus_equal(torture, status,
4944 : NT_STATUS_ACCESS_DENIED,
4945 : "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4946 6 : smb2_util_close(tree, fh);
4947 :
4948 : /* setup file for ZERO_DATA permissions tests */
4949 6 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
4950 : FNAME, &fh, 8192,
4951 : SEC_RIGHTS_FILE_ALL,
4952 : FILE_ATTRIBUTE_NORMAL);
4953 6 : torture_assert(torture, ok, "setup file");
4954 :
4955 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4956 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4957 6 : smb2_util_close(tree, fh);
4958 :
4959 : /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4960 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4961 : FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4962 : FILE_ATTRIBUTE_NORMAL);
4963 6 : torture_assert(torture, ok, "setup file");
4964 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4965 : 0, /* off */
4966 : 4096); /* beyond_final_zero */
4967 6 : torture_assert_ntstatus_equal(torture, status,
4968 : NT_STATUS_ACCESS_DENIED,
4969 : "zero_data permission");
4970 6 : smb2_util_close(tree, fh);
4971 :
4972 : /* attempt ZERO_DATA with only WRITE_DATA permission */
4973 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4974 : FNAME, &fh, SEC_FILE_WRITE_DATA,
4975 : FILE_ATTRIBUTE_NORMAL);
4976 6 : torture_assert(torture, ok, "setup file");
4977 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4978 : 0, /* off */
4979 : 4096); /* beyond_final_zero */
4980 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
4981 6 : smb2_util_close(tree, fh);
4982 :
4983 : /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4984 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4985 : FNAME, &fh, SEC_FILE_APPEND_DATA,
4986 : FILE_ATTRIBUTE_NORMAL);
4987 6 : torture_assert(torture, ok, "setup file");
4988 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4989 : 0, /* off */
4990 : 4096); /* beyond_final_zero */
4991 6 : torture_assert_ntstatus_equal(torture, status,
4992 : NT_STATUS_ACCESS_DENIED,
4993 : "zero_data permission");
4994 6 : smb2_util_close(tree, fh);
4995 :
4996 : /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4997 6 : ok = test_setup_open(torture, tree, tmp_ctx,
4998 : FNAME, &fh, SEC_FILE_WRITE_EA,
4999 : FILE_ATTRIBUTE_NORMAL);
5000 6 : torture_assert(torture, ok, "setup file");
5001 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5002 : 0, /* off */
5003 : 4096); /* beyond_final_zero */
5004 6 : torture_assert_ntstatus_equal(torture, status,
5005 : NT_STATUS_ACCESS_DENIED,
5006 : "zero_data permission");
5007 6 : smb2_util_close(tree, fh);
5008 :
5009 6 : talloc_free(tmp_ctx);
5010 6 : return true;
5011 : }
5012 :
5013 7 : static bool test_ioctl_sparse_lck(struct torture_context *torture,
5014 : struct smb2_tree *tree)
5015 : {
5016 : struct smb2_handle fh;
5017 : struct smb2_handle fh2;
5018 : NTSTATUS status;
5019 7 : uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5020 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5021 : bool ok;
5022 : bool is_sparse;
5023 : struct smb2_lock lck;
5024 : struct smb2_lock_element el[1];
5025 7 : struct file_alloced_range_buf *far_rsp = NULL;
5026 7 : uint64_t far_count = 0;
5027 :
5028 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
5029 : dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
5030 : FILE_ATTRIBUTE_NORMAL);
5031 7 : torture_assert(torture, ok, "setup file");
5032 :
5033 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5034 : FILE_SUPPORTS_SPARSE_FILES, &ok);
5035 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5036 7 : if (!ok) {
5037 1 : torture_skip(torture, "Sparse files not supported\n");
5038 : smb2_util_close(tree, fh);
5039 : }
5040 :
5041 : /* open and lock via separate fh2 */
5042 6 : status = torture_smb2_testfile(tree, FNAME, &fh2);
5043 6 : torture_assert_ntstatus_ok(torture, status, "2nd src open");
5044 :
5045 6 : lck.in.lock_count = 0x0001;
5046 6 : lck.in.lock_sequence = 0x00000000;
5047 6 : lck.in.file.handle = fh2;
5048 6 : lck.in.locks = el;
5049 6 : el[0].offset = 0;
5050 6 : el[0].length = dealloc_chunk_len;
5051 6 : el[0].reserved = 0;
5052 6 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
5053 :
5054 6 : status = smb2_lock(tree, &lck);
5055 6 : torture_assert_ntstatus_ok(torture, status, "lock");
5056 :
5057 : /* set sparse while locked */
5058 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5059 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5060 :
5061 6 : status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
5062 6 : torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
5063 6 : torture_assert(torture, is_sparse, "sparse attr after set");
5064 :
5065 : /* zero data over locked range should fail */
5066 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5067 : 0, /* off */
5068 : 4096); /* beyond_final_zero */
5069 6 : torture_assert_ntstatus_equal(torture, status,
5070 : NT_STATUS_FILE_LOCK_CONFLICT,
5071 : "zero_data locked");
5072 :
5073 : /* QAR over locked range should pass */
5074 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5075 : 0, /* off */
5076 : 4096, /* len */
5077 : &far_rsp, &far_count);
5078 6 : torture_assert_ntstatus_ok(torture, status,
5079 : "FSCTL_QUERY_ALLOCATED_RANGES locked");
5080 6 : torture_assert_u64_equal(torture, far_count, 1,
5081 : "unexpected response len");
5082 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5083 : "unexpected allocation");
5084 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5085 : 4096,
5086 : "unexpected far len");
5087 :
5088 : /* zero data over range past EOF should pass */
5089 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5090 : dealloc_chunk_len, /* off */
5091 6 : dealloc_chunk_len + 4096);
5092 6 : torture_assert_ntstatus_ok(torture, status,
5093 : "zero_data past EOF locked");
5094 :
5095 : /* QAR over range past EOF should pass */
5096 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5097 : dealloc_chunk_len, /* off */
5098 : 4096, /* len */
5099 : &far_rsp, &far_count);
5100 6 : torture_assert_ntstatus_ok(torture, status,
5101 : "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
5102 6 : torture_assert_u64_equal(torture, far_count, 0,
5103 : "unexpected response len");
5104 :
5105 6 : lck.in.lock_count = 0x0001;
5106 6 : lck.in.lock_sequence = 0x00000001;
5107 6 : lck.in.file.handle = fh2;
5108 6 : lck.in.locks = el;
5109 6 : el[0].offset = 0;
5110 6 : el[0].length = dealloc_chunk_len;
5111 6 : el[0].reserved = 0;
5112 6 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
5113 6 : status = smb2_lock(tree, &lck);
5114 6 : torture_assert_ntstatus_ok(torture, status, "unlock");
5115 :
5116 6 : smb2_util_close(tree, fh2);
5117 6 : smb2_util_close(tree, fh);
5118 6 : talloc_free(tmp_ctx);
5119 6 : return true;
5120 : }
5121 :
5122 : /* alleviate QAR off-by-one bug paranoia - help me ob1 */
5123 7 : static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
5124 : struct smb2_tree *tree)
5125 : {
5126 : struct smb2_handle fh;
5127 : NTSTATUS status;
5128 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5129 : bool ok;
5130 7 : uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5131 7 : struct file_alloced_range_buf *far_rsp = NULL;
5132 7 : uint64_t far_count = 0;
5133 :
5134 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
5135 : FNAME, &fh, dealloc_chunk_len * 2,
5136 : SEC_RIGHTS_FILE_ALL,
5137 : FILE_ATTRIBUTE_NORMAL);
5138 7 : torture_assert(torture, ok, "setup file");
5139 :
5140 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5141 : FILE_SUPPORTS_SPARSE_FILES, &ok);
5142 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5143 7 : if (!ok) {
5144 1 : torture_skip(torture, "Sparse files not supported\n");
5145 : smb2_util_close(tree, fh);
5146 : }
5147 :
5148 : /* non-sparse QAR with range one before EOF */
5149 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5150 : 0, /* off */
5151 6 : dealloc_chunk_len * 2 - 1, /* len */
5152 : &far_rsp, &far_count);
5153 6 : torture_assert_ntstatus_ok(torture, status,
5154 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5155 6 : torture_assert_u64_equal(torture, far_count, 1,
5156 : "unexpected response len");
5157 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5158 : "unexpected allocation");
5159 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5160 : dealloc_chunk_len * 2 - 1,
5161 : "unexpected far len");
5162 :
5163 : /* non-sparse QAR with range one after EOF */
5164 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5165 : 0, /* off */
5166 6 : dealloc_chunk_len * 2 + 1, /* len */
5167 : &far_rsp, &far_count);
5168 6 : torture_assert_ntstatus_ok(torture, status,
5169 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5170 6 : torture_assert_u64_equal(torture, far_count, 1,
5171 : "unexpected response len");
5172 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5173 : "unexpected allocation");
5174 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5175 : dealloc_chunk_len * 2,
5176 : "unexpected far len");
5177 :
5178 : /* non-sparse QAR with range one after EOF from off=1 */
5179 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5180 : 1, /* off */
5181 6 : dealloc_chunk_len * 2, /* len */
5182 : &far_rsp, &far_count);
5183 6 : torture_assert_ntstatus_ok(torture, status,
5184 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5185 6 : torture_assert_u64_equal(torture, far_count, 1,
5186 : "unexpected response len");
5187 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5188 : "unexpected allocation");
5189 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5190 : dealloc_chunk_len * 2 - 1,
5191 : "unexpected far len");
5192 :
5193 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5194 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5195 :
5196 : /* punch out second chunk */
5197 6 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5198 : dealloc_chunk_len, /* off */
5199 6 : dealloc_chunk_len * 2);
5200 6 : torture_assert_ntstatus_ok(torture, status, "zero_data");
5201 :
5202 : /* sparse QAR with range one before hole */
5203 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5204 : 0, /* off */
5205 6 : dealloc_chunk_len - 1, /* len */
5206 : &far_rsp, &far_count);
5207 6 : torture_assert_ntstatus_ok(torture, status,
5208 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5209 6 : torture_assert_u64_equal(torture, far_count, 1,
5210 : "unexpected response len");
5211 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5212 : "unexpected allocation");
5213 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5214 : dealloc_chunk_len - 1,
5215 : "unexpected far len");
5216 :
5217 : /* sparse QAR with range one after hole */
5218 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5219 : 0, /* off */
5220 6 : dealloc_chunk_len + 1, /* len */
5221 : &far_rsp, &far_count);
5222 6 : torture_assert_ntstatus_ok(torture, status,
5223 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5224 6 : torture_assert_u64_equal(torture, far_count, 1,
5225 : "unexpected response len");
5226 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
5227 : "unexpected allocation");
5228 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5229 : dealloc_chunk_len,
5230 : "unexpected far len");
5231 :
5232 : /* sparse QAR with range one after hole from off=1 */
5233 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5234 : 1, /* off */
5235 : dealloc_chunk_len, /* len */
5236 : &far_rsp, &far_count);
5237 6 : torture_assert_ntstatus_ok(torture, status,
5238 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5239 6 : torture_assert_u64_equal(torture, far_count, 1,
5240 : "unexpected response len");
5241 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
5242 : "unexpected allocation");
5243 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5244 : dealloc_chunk_len - 1,
5245 : "unexpected far len");
5246 :
5247 : /* sparse QAR with range one before EOF from off=chunk_len-1 */
5248 11 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5249 6 : dealloc_chunk_len - 1, /* off */
5250 : dealloc_chunk_len, /* len */
5251 : &far_rsp, &far_count);
5252 6 : torture_assert_ntstatus_ok(torture, status,
5253 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5254 6 : torture_assert_u64_equal(torture, far_count, 1,
5255 : "unexpected response len");
5256 6 : torture_assert_u64_equal(torture, far_rsp[0].file_off,
5257 : dealloc_chunk_len - 1,
5258 : "unexpected allocation");
5259 6 : torture_assert_u64_equal(torture, far_rsp[0].len,
5260 : 1, "unexpected far len");
5261 :
5262 : /* sparse QAR with range one after EOF from off=chunk_len+1 */
5263 11 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5264 6 : dealloc_chunk_len + 1, /* off */
5265 : dealloc_chunk_len, /* len */
5266 : &far_rsp, &far_count);
5267 6 : torture_assert_ntstatus_ok(torture, status,
5268 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5269 6 : torture_assert_u64_equal(torture, far_count, 0,
5270 : "unexpected response len");
5271 6 : smb2_util_close(tree, fh);
5272 6 : talloc_free(tmp_ctx);
5273 6 : return true;
5274 : }
5275 :
5276 : /* test QAR with multi-range responses */
5277 7 : static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
5278 : struct smb2_tree *tree)
5279 : {
5280 : struct smb2_handle fh;
5281 : NTSTATUS status;
5282 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5283 : bool ok;
5284 7 : uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
5285 : uint64_t this_off;
5286 : int i;
5287 7 : struct file_alloced_range_buf *far_rsp = NULL;
5288 7 : uint64_t far_count = 0;
5289 :
5290 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
5291 : FNAME, &fh, dealloc_chunk_len * 2,
5292 : SEC_RIGHTS_FILE_ALL,
5293 : FILE_ATTRIBUTE_NORMAL);
5294 7 : torture_assert(torture, ok, "setup file");
5295 :
5296 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5297 : FILE_SUPPORTS_SPARSE_FILES, &ok);
5298 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5299 7 : if (!ok) {
5300 1 : torture_skip(torture, "Sparse files not supported\n");
5301 : smb2_util_close(tree, fh);
5302 : }
5303 :
5304 6 : status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
5305 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
5306 :
5307 : /* each loop, write out two chunks and punch the first out */
5308 66 : for (i = 0; i < 10; i++) {
5309 60 : this_off = i * dealloc_chunk_len * 2;
5310 :
5311 60 : ok = write_pattern(torture, tree, tmp_ctx, fh,
5312 : this_off, /* off */
5313 : dealloc_chunk_len * 2, /* len */
5314 : this_off); /* pattern offset */
5315 60 : torture_assert(torture, ok, "write pattern");
5316 :
5317 60 : status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
5318 : this_off, /* off */
5319 60 : this_off + dealloc_chunk_len);
5320 60 : torture_assert_ntstatus_ok(torture, status, "zero_data");
5321 : }
5322 :
5323 : /* should now have one separate region for each iteration */
5324 6 : status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
5325 : 0,
5326 6 : 10 * dealloc_chunk_len * 2,
5327 : &far_rsp, &far_count);
5328 6 : torture_assert_ntstatus_ok(torture, status,
5329 : "FSCTL_QUERY_ALLOCATED_RANGES req failed");
5330 6 : if (far_count == 1) {
5331 0 : torture_comment(torture, "this FS doesn't deallocate 64K"
5332 : "zeroed ranges in sparse files\n");
5333 0 : return true; /* FS specific, not a failure */
5334 : }
5335 6 : torture_assert_u64_equal(torture, far_count, 10,
5336 : "unexpected response len");
5337 66 : for (i = 0; i < 10; i++) {
5338 60 : this_off = i * dealloc_chunk_len * 2;
5339 :
5340 60 : torture_assert_u64_equal(torture, far_rsp[i].file_off,
5341 : this_off + dealloc_chunk_len,
5342 : "unexpected allocation");
5343 60 : torture_assert_u64_equal(torture, far_rsp[i].len,
5344 : dealloc_chunk_len,
5345 : "unexpected far len");
5346 : }
5347 :
5348 6 : smb2_util_close(tree, fh);
5349 6 : talloc_free(tmp_ctx);
5350 6 : return true;
5351 : }
5352 :
5353 7 : static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
5354 : struct smb2_tree *tree)
5355 : {
5356 : struct smb2_handle fh;
5357 : union smb_ioctl ioctl;
5358 : struct file_alloced_range_buf far_buf;
5359 : NTSTATUS status;
5360 : enum ndr_err_code ndr_ret;
5361 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5362 : bool ok;
5363 :
5364 7 : ok = test_setup_create_fill(torture, tree, tmp_ctx,
5365 : FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
5366 : FILE_ATTRIBUTE_NORMAL);
5367 7 : torture_assert(torture, ok, "setup file");
5368 :
5369 7 : status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
5370 : FILE_SUPPORTS_SPARSE_FILES, &ok);
5371 7 : torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
5372 7 : if (!ok) {
5373 1 : smb2_util_close(tree, fh);
5374 1 : torture_skip(torture, "Sparse files not supported\n");
5375 : }
5376 :
5377 : /* no allocated ranges, no space for range response, should pass */
5378 6 : ZERO_STRUCT(ioctl);
5379 6 : ioctl.smb2.level = RAW_IOCTL_SMB2;
5380 6 : ioctl.smb2.in.file.handle = fh;
5381 6 : ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
5382 6 : ioctl.smb2.in.max_output_response = 1024;
5383 6 : ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5384 :
5385 : /* off + length wraps around to 511 */
5386 6 : far_buf.file_off = 512;
5387 6 : far_buf.len = 0xffffffffffffffffLL;
5388 6 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5389 : &far_buf,
5390 : (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
5391 6 : torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
5392 :
5393 6 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5394 6 : torture_assert_ntstatus_equal(torture, status,
5395 : NT_STATUS_INVALID_PARAMETER,
5396 : "FSCTL_QUERY_ALLOCATED_RANGES overflow");
5397 :
5398 6 : return true;
5399 : }
5400 :
5401 7 : static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
5402 : struct smb2_tree *tree,
5403 : TALLOC_CTX *mem_ctx,
5404 : struct smb2_handle *fh,
5405 : bool *trim_support)
5406 : {
5407 : NTSTATUS status;
5408 : union smb_fsinfo info;
5409 :
5410 7 : ZERO_STRUCT(info);
5411 7 : info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
5412 7 : info.generic.handle = *fh;
5413 7 : status = smb2_getinfo_fs(tree, tree, &info);
5414 7 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
5415 : /*
5416 : * Windows < Server 2012, 8 etc. don't support this info level
5417 : * or the trim ioctl. Ignore the error and let the caller skip.
5418 : */
5419 0 : *trim_support = false;
5420 0 : return NT_STATUS_OK;
5421 7 : } else if (!NT_STATUS_IS_OK(status)) {
5422 0 : return status;
5423 : }
5424 :
5425 7 : torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
5426 : "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
5427 7 : (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
5428 7 : (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
5429 7 : (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
5430 7 : (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
5431 7 : (unsigned)info.sector_size_info.out.flags,
5432 7 : (unsigned)info.sector_size_info.out.byte_off_sector_align,
5433 7 : (unsigned)info.sector_size_info.out.byte_off_partition_align);
5434 :
5435 7 : if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
5436 0 : *trim_support = true;
5437 : } else {
5438 7 : *trim_support = false;
5439 : }
5440 7 : return NT_STATUS_OK;
5441 : }
5442 :
5443 7 : static bool test_setup_trim(struct torture_context *torture,
5444 : struct smb2_tree *tree,
5445 : TALLOC_CTX *mem_ctx,
5446 : uint32_t num_ranges,
5447 : struct smb2_handle *fh,
5448 : uint64_t file_size,
5449 : uint32_t desired_access,
5450 : struct fsctl_file_level_trim_req *trim_req,
5451 : union smb_ioctl *ioctl)
5452 : {
5453 : bool ok;
5454 :
5455 7 : ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
5456 : fh, file_size, desired_access,
5457 : FILE_ATTRIBUTE_NORMAL);
5458 7 : torture_assert(torture, ok, "src file create fill");
5459 :
5460 7 : ZERO_STRUCTPN(ioctl);
5461 7 : ioctl->smb2.level = RAW_IOCTL_SMB2;
5462 7 : ioctl->smb2.in.file.handle = *fh;
5463 7 : ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
5464 : ioctl->smb2.in.max_output_response
5465 7 : = sizeof(struct fsctl_file_level_trim_rsp);
5466 7 : ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5467 :
5468 7 : ZERO_STRUCTPN(trim_req);
5469 : /* leave key as zero for now. TODO test locking with differing keys */
5470 7 : trim_req->num_ranges = num_ranges;
5471 7 : trim_req->ranges = talloc_zero_array(mem_ctx,
5472 : struct file_level_trim_range,
5473 : num_ranges);
5474 7 : torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
5475 :
5476 7 : return true;
5477 : }
5478 :
5479 7 : static bool test_ioctl_trim_simple(struct torture_context *torture,
5480 : struct smb2_tree *tree)
5481 : {
5482 : struct smb2_handle fh;
5483 : NTSTATUS status;
5484 : union smb_ioctl ioctl;
5485 : bool trim_supported;
5486 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5487 : struct fsctl_file_level_trim_req trim_req;
5488 : struct fsctl_file_level_trim_rsp trim_rsp;
5489 7 : uint64_t trim_chunk_len = 64 * 1024; /* trim 64K chunks */
5490 : enum ndr_err_code ndr_ret;
5491 : bool ok;
5492 :
5493 7 : ok = test_setup_trim(torture, tree, tmp_ctx,
5494 : 1, /* 1 range */
5495 : &fh, 2 * trim_chunk_len, /* fill 128K file */
5496 : SEC_RIGHTS_FILE_ALL,
5497 : &trim_req,
5498 : &ioctl);
5499 7 : if (!ok) {
5500 0 : torture_fail(torture, "setup trim error");
5501 : }
5502 :
5503 7 : status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
5504 : &trim_supported);
5505 7 : torture_assert_ntstatus_ok(torture, status, "fsinfo");
5506 7 : if (!trim_supported) {
5507 7 : smb2_util_close(tree, fh);
5508 7 : talloc_free(tmp_ctx);
5509 7 : torture_skip(torture, "trim not supported\n");
5510 : }
5511 :
5512 : /* trim first chunk, leave second */
5513 0 : trim_req.ranges[0].off = 0;
5514 0 : trim_req.ranges[0].len = trim_chunk_len;
5515 :
5516 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
5517 : (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
5518 0 : torture_assert_ndr_success(torture, ndr_ret,
5519 : "ndr_push_fsctl_file_level_trim_req");
5520 :
5521 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5522 0 : torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
5523 :
5524 0 : ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
5525 : &trim_rsp,
5526 : (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
5527 0 : torture_assert_ndr_success(torture, ndr_ret,
5528 : "ndr_pull_fsctl_file_level_trim_rsp");
5529 :
5530 0 : torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
5531 :
5532 : /* second half of the file should remain consitent */
5533 0 : ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
5534 : trim_chunk_len, trim_chunk_len);
5535 0 : torture_assert(torture, ok, "non-trimmed range inconsistent");
5536 :
5537 0 : return true;
5538 : }
5539 :
5540 98 : static bool test_setup_dup_extents(struct torture_context *tctx,
5541 : struct smb2_tree *tree,
5542 : TALLOC_CTX *mem_ctx,
5543 : struct smb2_handle *src_h,
5544 : uint64_t src_size,
5545 : uint32_t src_desired_access,
5546 : struct smb2_handle *dest_h,
5547 : uint64_t dest_size,
5548 : uint32_t dest_desired_access,
5549 : struct fsctl_dup_extents_to_file *dup_ext_buf,
5550 : union smb_ioctl *ioctl)
5551 : {
5552 : bool ok;
5553 :
5554 98 : ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
5555 : src_h, src_size, src_desired_access,
5556 : FILE_ATTRIBUTE_NORMAL);
5557 98 : torture_assert(tctx, ok, "src file create fill");
5558 :
5559 98 : ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
5560 : dest_h, dest_size, dest_desired_access,
5561 : FILE_ATTRIBUTE_NORMAL);
5562 98 : torture_assert(tctx, ok, "dest file create fill");
5563 :
5564 98 : ZERO_STRUCTPN(ioctl);
5565 98 : ioctl->smb2.level = RAW_IOCTL_SMB2;
5566 98 : ioctl->smb2.in.file.handle = *dest_h;
5567 98 : ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
5568 98 : ioctl->smb2.in.max_output_response = 0;
5569 98 : ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
5570 :
5571 98 : ZERO_STRUCTPN(dup_ext_buf);
5572 98 : smb2_push_handle(dup_ext_buf->source_fid, src_h);
5573 :
5574 98 : return true;
5575 : }
5576 :
5577 7 : static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
5578 : struct smb2_tree *tree)
5579 : {
5580 : struct smb2_handle src_h;
5581 : struct smb2_handle dest_h;
5582 : NTSTATUS status;
5583 : union smb_ioctl ioctl;
5584 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5585 : struct fsctl_dup_extents_to_file dup_ext_buf;
5586 : enum ndr_err_code ndr_ret;
5587 : union smb_fileinfo io;
5588 : union smb_setfileinfo sinfo;
5589 : bool ok;
5590 :
5591 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5592 : &src_h, 4096, /* fill 4096 byte src file */
5593 : SEC_RIGHTS_FILE_ALL,
5594 : &dest_h, 0, /* 0 byte dest file */
5595 : SEC_RIGHTS_FILE_ALL,
5596 : &dup_ext_buf,
5597 : &ioctl);
5598 7 : if (!ok) {
5599 0 : torture_fail(tctx, "setup dup extents error");
5600 : }
5601 :
5602 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5603 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5604 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5605 7 : if (!ok) {
5606 7 : smb2_util_close(tree, src_h);
5607 7 : smb2_util_close(tree, dest_h);
5608 7 : talloc_free(tmp_ctx);
5609 7 : torture_skip(tctx, "block refcounting not supported\n");
5610 : }
5611 :
5612 : /* extend dest to match src len */
5613 0 : ZERO_STRUCT(sinfo);
5614 0 : sinfo.end_of_file_info.level =
5615 : RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5616 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
5617 0 : sinfo.end_of_file_info.in.size = 4096;
5618 0 : status = smb2_setinfo_file(tree, &sinfo);
5619 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5620 :
5621 : /* copy all src file data */
5622 0 : dup_ext_buf.source_off = 0;
5623 0 : dup_ext_buf.target_off = 0;
5624 0 : dup_ext_buf.byte_count = 4096;
5625 :
5626 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5627 : &dup_ext_buf,
5628 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5629 0 : torture_assert_ndr_success(tctx, ndr_ret,
5630 : "ndr_push_fsctl_dup_extents_to_file");
5631 :
5632 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5633 0 : torture_assert_ntstatus_ok(tctx, status,
5634 : "FSCTL_DUP_EXTENTS_TO_FILE");
5635 :
5636 : /* the file size shouldn't have been changed by this operation! */
5637 0 : ZERO_STRUCT(io);
5638 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5639 0 : io.generic.in.file.handle = dest_h;
5640 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5641 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5642 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5643 : 4096, "size after IO");
5644 :
5645 0 : smb2_util_close(tree, src_h);
5646 0 : smb2_util_close(tree, dest_h);
5647 :
5648 : /* reopen for pattern check */
5649 0 : ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
5650 : SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5651 0 : torture_assert_ntstatus_ok(tctx, status, "src open after dup");
5652 0 : ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5653 : SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5654 0 : torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
5655 :
5656 0 : ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5657 0 : if (!ok) {
5658 0 : torture_fail(tctx, "inconsistent src file data");
5659 : }
5660 :
5661 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5662 0 : if (!ok) {
5663 0 : torture_fail(tctx, "inconsistent dest file data");
5664 : }
5665 :
5666 0 : smb2_util_close(tree, src_h);
5667 0 : smb2_util_close(tree, dest_h);
5668 0 : talloc_free(tmp_ctx);
5669 0 : return true;
5670 : }
5671 :
5672 7 : static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
5673 : struct smb2_tree *tree)
5674 : {
5675 : struct smb2_handle src_h;
5676 : struct smb2_handle dest_h;
5677 : NTSTATUS status;
5678 : union smb_ioctl ioctl;
5679 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5680 : struct fsctl_dup_extents_to_file dup_ext_buf;
5681 : enum ndr_err_code ndr_ret;
5682 : union smb_fileinfo io;
5683 : union smb_setfileinfo sinfo;
5684 : bool ok;
5685 :
5686 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5687 : &src_h, 32768, /* fill 32768 byte src file */
5688 : SEC_RIGHTS_FILE_ALL,
5689 : &dest_h, 0, /* 0 byte dest file */
5690 : SEC_RIGHTS_FILE_ALL,
5691 : &dup_ext_buf,
5692 : &ioctl);
5693 7 : if (!ok) {
5694 0 : torture_fail(tctx, "setup dup extents error");
5695 : }
5696 :
5697 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5698 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5699 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5700 7 : if (!ok) {
5701 7 : smb2_util_close(tree, src_h);
5702 7 : smb2_util_close(tree, dest_h);
5703 7 : talloc_free(tmp_ctx);
5704 7 : torture_skip(tctx, "block refcounting not supported\n");
5705 : }
5706 :
5707 0 : ZERO_STRUCT(io);
5708 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5709 0 : io.generic.in.file.handle = dest_h;
5710 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5711 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5712 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5713 : 0, "size after IO");
5714 :
5715 : /* copy all src file data */
5716 0 : dup_ext_buf.source_off = 0;
5717 0 : dup_ext_buf.target_off = 0;
5718 0 : dup_ext_buf.byte_count = 32768;
5719 :
5720 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5721 : &dup_ext_buf,
5722 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5723 0 : torture_assert_ndr_success(tctx, ndr_ret,
5724 : "ndr_push_fsctl_dup_extents_to_file");
5725 :
5726 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5727 : #if 0
5728 : /*
5729 : * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5730 : * passes against WS2016 RTM!
5731 : */
5732 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5733 : "FSCTL_DUP_EXTENTS_TO_FILE");
5734 : #endif
5735 :
5736 : /* the file sizes shouldn't have been changed */
5737 0 : ZERO_STRUCT(io);
5738 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5739 0 : io.generic.in.file.handle = src_h;
5740 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5741 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5742 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5743 : 32768, "size after IO");
5744 :
5745 0 : ZERO_STRUCT(io);
5746 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5747 0 : io.generic.in.file.handle = dest_h;
5748 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5749 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5750 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5751 : 0, "size after IO");
5752 :
5753 : /* extend dest */
5754 0 : ZERO_STRUCT(sinfo);
5755 0 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5756 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
5757 0 : sinfo.end_of_file_info.in.size = 32768;
5758 0 : status = smb2_setinfo_file(tree, &sinfo);
5759 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5760 :
5761 0 : ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5762 0 : if (!ok) {
5763 0 : torture_fail(tctx, "inconsistent file data");
5764 : }
5765 :
5766 : /* reissue ioctl, now with enough space */
5767 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5768 0 : torture_assert_ntstatus_ok(tctx, status,
5769 : "FSCTL_DUP_EXTENTS_TO_FILE");
5770 :
5771 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5772 0 : if (!ok) {
5773 0 : torture_fail(tctx, "inconsistent file data");
5774 : }
5775 :
5776 0 : smb2_util_close(tree, src_h);
5777 0 : smb2_util_close(tree, dest_h);
5778 0 : talloc_free(tmp_ctx);
5779 0 : return true;
5780 : }
5781 :
5782 7 : static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5783 : struct smb2_tree *tree)
5784 : {
5785 : struct smb2_handle src_h;
5786 : struct smb2_handle dest_h;
5787 : NTSTATUS status;
5788 : union smb_ioctl ioctl;
5789 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5790 : struct fsctl_dup_extents_to_file dup_ext_buf;
5791 : enum ndr_err_code ndr_ret;
5792 : union smb_fileinfo io;
5793 : bool ok;
5794 :
5795 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5796 : &src_h, 32768, /* fill 32768 byte src file */
5797 : SEC_RIGHTS_FILE_ALL,
5798 : &dest_h, 0, /* 0 byte dest file */
5799 : SEC_RIGHTS_FILE_ALL,
5800 : &dup_ext_buf,
5801 : &ioctl);
5802 7 : if (!ok) {
5803 0 : torture_fail(tctx, "setup dup extents error");
5804 : }
5805 :
5806 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5807 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5808 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5809 7 : if (!ok) {
5810 7 : smb2_util_close(tree, src_h);
5811 7 : smb2_util_close(tree, dest_h);
5812 7 : talloc_free(tmp_ctx);
5813 7 : torture_skip(tctx, "block refcounting not supported\n");
5814 : }
5815 :
5816 0 : ZERO_STRUCT(io);
5817 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5818 0 : io.generic.in.file.handle = dest_h;
5819 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5820 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5821 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5822 : 0, "size after IO");
5823 :
5824 : /* exceed src file len */
5825 0 : dup_ext_buf.source_off = 0;
5826 0 : dup_ext_buf.target_off = 0;
5827 0 : dup_ext_buf.byte_count = 32768 * 2;
5828 :
5829 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5830 : &dup_ext_buf,
5831 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5832 0 : torture_assert_ndr_success(tctx, ndr_ret,
5833 : "ndr_push_fsctl_dup_extents_to_file");
5834 :
5835 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5836 0 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5837 : "FSCTL_DUP_EXTENTS_TO_FILE");
5838 :
5839 : /* the file sizes shouldn't have been changed */
5840 0 : ZERO_STRUCT(io);
5841 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5842 0 : io.generic.in.file.handle = src_h;
5843 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5844 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5845 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5846 : 32768, "size after IO");
5847 :
5848 0 : ZERO_STRUCT(io);
5849 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5850 0 : io.generic.in.file.handle = dest_h;
5851 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5852 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5853 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5854 : 0, "size after IO");
5855 :
5856 0 : smb2_util_close(tree, src_h);
5857 0 : smb2_util_close(tree, dest_h);
5858 0 : talloc_free(tmp_ctx);
5859 0 : return true;
5860 : }
5861 :
5862 7 : static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5863 : struct smb2_tree *tree)
5864 : {
5865 : struct smb2_handle src_h;
5866 : struct smb2_handle dest_h;
5867 : NTSTATUS status;
5868 : union smb_ioctl ioctl;
5869 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5870 : struct fsctl_dup_extents_to_file dup_ext_buf;
5871 : enum ndr_err_code ndr_ret;
5872 : union smb_fileinfo io;
5873 : bool ok;
5874 :
5875 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5876 : &src_h, 32768, /* fill 32768 byte src file */
5877 : SEC_RIGHTS_FILE_ALL,
5878 : &dest_h, 0, /* 0 byte dest file */
5879 : SEC_RIGHTS_FILE_ALL,
5880 : &dup_ext_buf,
5881 : &ioctl);
5882 7 : if (!ok) {
5883 0 : torture_fail(tctx, "setup dup extents error");
5884 : }
5885 :
5886 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5887 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5888 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5889 7 : if (!ok) {
5890 7 : smb2_util_close(tree, src_h);
5891 7 : smb2_util_close(tree, dest_h);
5892 7 : talloc_free(tmp_ctx);
5893 7 : torture_skip(tctx, "block refcounting not supported\n");
5894 : }
5895 :
5896 0 : ZERO_STRUCT(io);
5897 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5898 0 : io.generic.in.file.handle = dest_h;
5899 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5900 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5901 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5902 : 0, "size after IO");
5903 :
5904 0 : dup_ext_buf.source_off = 0;
5905 0 : dup_ext_buf.target_off = 0;
5906 0 : dup_ext_buf.byte_count = 0;
5907 :
5908 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5909 : &dup_ext_buf,
5910 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5911 0 : torture_assert_ndr_success(tctx, ndr_ret,
5912 : "ndr_push_fsctl_dup_extents_to_file");
5913 :
5914 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5915 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5916 :
5917 : /* the file sizes shouldn't have been changed */
5918 0 : ZERO_STRUCT(io);
5919 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5920 0 : io.generic.in.file.handle = src_h;
5921 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5922 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5923 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5924 : 32768, "size after IO");
5925 :
5926 0 : ZERO_STRUCT(io);
5927 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5928 0 : io.generic.in.file.handle = dest_h;
5929 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
5930 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
5931 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5932 : 0, "size after IO");
5933 :
5934 0 : smb2_util_close(tree, src_h);
5935 0 : smb2_util_close(tree, dest_h);
5936 0 : talloc_free(tmp_ctx);
5937 0 : return true;
5938 : }
5939 :
5940 7 : static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5941 : struct smb2_tree *tree)
5942 : {
5943 : struct smb2_handle src_h;
5944 : struct smb2_handle dest_h;
5945 : NTSTATUS status;
5946 : union smb_ioctl ioctl;
5947 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
5948 : struct fsctl_dup_extents_to_file dup_ext_buf;
5949 : enum ndr_err_code ndr_ret;
5950 : union smb_setfileinfo sinfo;
5951 : bool ok;
5952 :
5953 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5954 : &src_h, 0, /* filled after sparse flag */
5955 : SEC_RIGHTS_FILE_ALL,
5956 : &dest_h, 0, /* 0 byte dest file */
5957 : SEC_RIGHTS_FILE_ALL,
5958 : &dup_ext_buf,
5959 : &ioctl);
5960 7 : if (!ok) {
5961 0 : torture_fail(tctx, "setup dup extents error");
5962 : }
5963 :
5964 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5965 : FILE_SUPPORTS_BLOCK_REFCOUNTING
5966 : | FILE_SUPPORTS_SPARSE_FILES, &ok);
5967 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5968 7 : if (!ok) {
5969 7 : smb2_util_close(tree, src_h);
5970 7 : smb2_util_close(tree, dest_h);
5971 7 : talloc_free(tmp_ctx);
5972 7 : torture_skip(tctx,
5973 : "block refcounting and sparse files not supported\n");
5974 : }
5975 :
5976 : /* set sparse flag on src */
5977 0 : status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5978 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5979 :
5980 0 : ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5981 0 : torture_assert(tctx, ok, "write pattern");
5982 :
5983 : /* extend dest */
5984 0 : ZERO_STRUCT(sinfo);
5985 0 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5986 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
5987 0 : sinfo.end_of_file_info.in.size = 4096;
5988 0 : status = smb2_setinfo_file(tree, &sinfo);
5989 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5990 :
5991 : /* copy all src file data */
5992 0 : dup_ext_buf.source_off = 0;
5993 0 : dup_ext_buf.target_off = 0;
5994 0 : dup_ext_buf.byte_count = 4096;
5995 :
5996 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5997 : &dup_ext_buf,
5998 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5999 0 : torture_assert_ndr_success(tctx, ndr_ret,
6000 : "ndr_push_fsctl_dup_extents_to_file");
6001 :
6002 : /*
6003 : * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6004 : * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6005 : * is a non-sparse file.
6006 : */
6007 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6008 0 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6009 : "FSCTL_DUP_EXTENTS_TO_FILE");
6010 :
6011 0 : smb2_util_close(tree, src_h);
6012 0 : smb2_util_close(tree, dest_h);
6013 0 : talloc_free(tmp_ctx);
6014 0 : return true;
6015 : }
6016 :
6017 7 : static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
6018 : struct smb2_tree *tree)
6019 : {
6020 : struct smb2_handle src_h;
6021 : struct smb2_handle dest_h;
6022 : NTSTATUS status;
6023 : union smb_ioctl ioctl;
6024 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6025 : struct fsctl_dup_extents_to_file dup_ext_buf;
6026 : enum ndr_err_code ndr_ret;
6027 : union smb_setfileinfo sinfo;
6028 : bool ok;
6029 :
6030 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6031 : &src_h, 4096, /* fill 4096 byte src file */
6032 : SEC_RIGHTS_FILE_ALL,
6033 : &dest_h, 0, /* 0 byte dest file */
6034 : SEC_RIGHTS_FILE_ALL,
6035 : &dup_ext_buf,
6036 : &ioctl);
6037 7 : if (!ok) {
6038 0 : torture_fail(tctx, "setup dup extents error");
6039 : }
6040 :
6041 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6042 : FILE_SUPPORTS_BLOCK_REFCOUNTING
6043 : | FILE_SUPPORTS_SPARSE_FILES, &ok);
6044 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6045 7 : if (!ok) {
6046 7 : smb2_util_close(tree, src_h);
6047 7 : smb2_util_close(tree, dest_h);
6048 7 : talloc_free(tmp_ctx);
6049 7 : torture_skip(tctx,
6050 : "block refcounting and sparse files not supported\n");
6051 : }
6052 :
6053 : /* set sparse flag on dest */
6054 0 : status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6055 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6056 :
6057 : /* extend dest */
6058 0 : ZERO_STRUCT(sinfo);
6059 0 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6060 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
6061 0 : sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
6062 0 : status = smb2_setinfo_file(tree, &sinfo);
6063 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6064 :
6065 : /* copy all src file data */
6066 0 : dup_ext_buf.source_off = 0;
6067 0 : dup_ext_buf.target_off = 0;
6068 0 : dup_ext_buf.byte_count = 4096;
6069 :
6070 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6071 : &dup_ext_buf,
6072 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6073 0 : torture_assert_ndr_success(tctx, ndr_ret,
6074 : "ndr_push_fsctl_dup_extents_to_file");
6075 :
6076 : /*
6077 : * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
6078 : * Reply... STATUS_NOT_SUPPORTED: Target file is sparse, while source
6079 : * is a non-sparse file.
6080 : */
6081 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6082 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6083 :
6084 0 : smb2_util_close(tree, src_h);
6085 0 : smb2_util_close(tree, dest_h);
6086 0 : talloc_free(tmp_ctx);
6087 0 : return true;
6088 : }
6089 :
6090 7 : static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
6091 : struct smb2_tree *tree)
6092 : {
6093 : struct smb2_handle src_h;
6094 : struct smb2_handle dest_h;
6095 : NTSTATUS status;
6096 : union smb_ioctl ioctl;
6097 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6098 : struct fsctl_dup_extents_to_file dup_ext_buf;
6099 : enum ndr_err_code ndr_ret;
6100 : union smb_setfileinfo sinfo;
6101 : bool ok;
6102 :
6103 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6104 : &src_h, 0, /* fill 4096 byte src file */
6105 : SEC_RIGHTS_FILE_ALL,
6106 : &dest_h, 0, /* 0 byte dest file */
6107 : SEC_RIGHTS_FILE_ALL,
6108 : &dup_ext_buf,
6109 : &ioctl);
6110 7 : if (!ok) {
6111 0 : torture_fail(tctx, "setup dup extents error");
6112 : }
6113 :
6114 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6115 : FILE_SUPPORTS_BLOCK_REFCOUNTING
6116 : | FILE_SUPPORTS_SPARSE_FILES, &ok);
6117 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6118 7 : if (!ok) {
6119 7 : smb2_util_close(tree, src_h);
6120 7 : smb2_util_close(tree, dest_h);
6121 7 : talloc_free(tmp_ctx);
6122 7 : torture_skip(tctx,
6123 : "block refcounting and sparse files not supported\n");
6124 : }
6125 :
6126 : /* set sparse flag on src and dest */
6127 0 : status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
6128 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6129 0 : status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
6130 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
6131 :
6132 0 : ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6133 0 : torture_assert(tctx, ok, "write pattern");
6134 :
6135 : /* extend dest */
6136 0 : ZERO_STRUCT(sinfo);
6137 0 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6138 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
6139 0 : sinfo.end_of_file_info.in.size = 4096;
6140 0 : status = smb2_setinfo_file(tree, &sinfo);
6141 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6142 :
6143 : /* copy all src file data */
6144 0 : dup_ext_buf.source_off = 0;
6145 0 : dup_ext_buf.target_off = 0;
6146 0 : dup_ext_buf.byte_count = 4096;
6147 :
6148 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6149 : &dup_ext_buf,
6150 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6151 0 : torture_assert_ndr_success(tctx, ndr_ret,
6152 : "ndr_push_fsctl_dup_extents_to_file");
6153 :
6154 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6155 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6156 :
6157 0 : smb2_util_close(tree, src_h);
6158 0 : smb2_util_close(tree, dest_h);
6159 :
6160 : /* reopen for pattern check */
6161 0 : ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
6162 : SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
6163 0 : torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
6164 :
6165 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6166 0 : if (!ok) {
6167 0 : torture_fail(tctx, "inconsistent file data");
6168 : }
6169 :
6170 0 : smb2_util_close(tree, dest_h);
6171 0 : talloc_free(tmp_ctx);
6172 0 : return true;
6173 : }
6174 :
6175 7 : static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
6176 : struct smb2_tree *tree)
6177 : {
6178 : struct smb2_handle src_h;
6179 : struct smb2_handle dest_h;
6180 : NTSTATUS status;
6181 : union smb_ioctl ioctl;
6182 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6183 : struct fsctl_dup_extents_to_file dup_ext_buf;
6184 : enum ndr_err_code ndr_ret;
6185 : union smb_fileinfo io;
6186 : bool ok;
6187 :
6188 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6189 : &src_h, 32768, /* fill 32768 byte src file */
6190 : SEC_RIGHTS_FILE_ALL,
6191 : &dest_h, 0,
6192 : SEC_RIGHTS_FILE_ALL,
6193 : &dup_ext_buf,
6194 : &ioctl);
6195 7 : if (!ok) {
6196 0 : torture_fail(tctx, "setup dup extents error");
6197 : }
6198 : /* dest_h not needed for this test */
6199 7 : smb2_util_close(tree, dest_h);
6200 :
6201 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6202 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6203 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6204 7 : if (!ok) {
6205 7 : smb2_util_close(tree, src_h);
6206 7 : talloc_free(tmp_ctx);
6207 7 : torture_skip(tctx, "block refcounting not supported\n");
6208 : }
6209 :
6210 : /* src and dest are the same file handle */
6211 0 : ioctl.smb2.in.file.handle = src_h;
6212 :
6213 : /* no overlap between src and tgt */
6214 0 : dup_ext_buf.source_off = 0;
6215 0 : dup_ext_buf.target_off = 16384;
6216 0 : dup_ext_buf.byte_count = 16384;
6217 :
6218 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6219 : &dup_ext_buf,
6220 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6221 0 : torture_assert_ndr_success(tctx, ndr_ret,
6222 : "ndr_push_fsctl_dup_extents_to_file");
6223 :
6224 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6225 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6226 :
6227 : /* the file size shouldn't have been changed */
6228 0 : ZERO_STRUCT(io);
6229 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6230 0 : io.generic.in.file.handle = src_h;
6231 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
6232 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
6233 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6234 : 32768, "size after IO");
6235 :
6236 0 : ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
6237 0 : if (!ok) {
6238 0 : torture_fail(tctx, "inconsistent file data");
6239 : }
6240 0 : ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
6241 0 : if (!ok) {
6242 0 : torture_fail(tctx, "inconsistent file data");
6243 : }
6244 :
6245 0 : smb2_util_close(tree, src_h);
6246 0 : talloc_free(tmp_ctx);
6247 0 : return true;
6248 : }
6249 :
6250 : /*
6251 : * unlike copy-chunk, dup extents doesn't support overlapping ranges between
6252 : * source and target. This makes it a *lot* cleaner to implement on the server.
6253 : */
6254 : static bool
6255 7 : test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
6256 : struct smb2_tree *tree)
6257 : {
6258 : struct smb2_handle src_h;
6259 : struct smb2_handle dest_h;
6260 : NTSTATUS status;
6261 : union smb_ioctl ioctl;
6262 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6263 : struct fsctl_dup_extents_to_file dup_ext_buf;
6264 : enum ndr_err_code ndr_ret;
6265 : union smb_fileinfo io;
6266 : bool ok;
6267 :
6268 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6269 : &src_h, 32768, /* fill 32768 byte src file */
6270 : SEC_RIGHTS_FILE_ALL,
6271 : &dest_h, 0,
6272 : SEC_RIGHTS_FILE_ALL,
6273 : &dup_ext_buf,
6274 : &ioctl);
6275 7 : if (!ok) {
6276 0 : torture_fail(tctx, "setup dup extents error");
6277 : }
6278 : /* dest_h not needed for this test */
6279 7 : smb2_util_close(tree, dest_h);
6280 :
6281 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6282 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6283 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6284 7 : if (!ok) {
6285 7 : smb2_util_close(tree, src_h);
6286 7 : talloc_free(tmp_ctx);
6287 7 : torture_skip(tctx, "block refcounting not supported\n");
6288 : }
6289 :
6290 : /* src and dest are the same file handle */
6291 0 : ioctl.smb2.in.file.handle = src_h;
6292 :
6293 : /* 8K overlap between src and tgt */
6294 0 : dup_ext_buf.source_off = 0;
6295 0 : dup_ext_buf.target_off = 8192;
6296 0 : dup_ext_buf.byte_count = 16384;
6297 :
6298 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6299 : &dup_ext_buf,
6300 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6301 0 : torture_assert_ndr_success(tctx, ndr_ret,
6302 : "ndr_push_fsctl_dup_extents_to_file");
6303 :
6304 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6305 0 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
6306 : "FSCTL_DUP_EXTENTS_TO_FILE");
6307 :
6308 : /* the file size and data should match beforehand */
6309 0 : ZERO_STRUCT(io);
6310 0 : io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
6311 0 : io.generic.in.file.handle = src_h;
6312 0 : status = smb2_getinfo_file(tree, tmp_ctx, &io);
6313 0 : torture_assert_ntstatus_ok(tctx, status, "getinfo");
6314 0 : torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
6315 : 32768, "size after IO");
6316 :
6317 0 : ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6318 0 : if (!ok) {
6319 0 : torture_fail(tctx, "inconsistent file data");
6320 : }
6321 :
6322 0 : smb2_util_close(tree, src_h);
6323 0 : talloc_free(tmp_ctx);
6324 0 : return true;
6325 : }
6326 :
6327 : /*
6328 : * The compression tests won't run against Windows servers yet - ReFS doesn't
6329 : * (yet) offer support for compression.
6330 : */
6331 7 : static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
6332 : struct smb2_tree *tree)
6333 : {
6334 : struct smb2_handle src_h;
6335 : struct smb2_handle dest_h;
6336 : NTSTATUS status;
6337 : union smb_ioctl ioctl;
6338 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6339 : struct fsctl_dup_extents_to_file dup_ext_buf;
6340 : enum ndr_err_code ndr_ret;
6341 : union smb_setfileinfo sinfo;
6342 : bool ok;
6343 :
6344 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6345 : &src_h, 0, /* filled after compressed flag */
6346 : SEC_RIGHTS_FILE_ALL,
6347 : &dest_h, 0,
6348 : SEC_RIGHTS_FILE_ALL,
6349 : &dup_ext_buf,
6350 : &ioctl);
6351 7 : if (!ok) {
6352 0 : torture_fail(tctx, "setup dup extents error");
6353 : }
6354 :
6355 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6356 : FILE_SUPPORTS_BLOCK_REFCOUNTING
6357 : | FILE_FILE_COMPRESSION, &ok);
6358 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6359 7 : if (!ok) {
6360 7 : smb2_util_close(tree, src_h);
6361 7 : smb2_util_close(tree, dest_h);
6362 7 : talloc_free(tmp_ctx);
6363 7 : torture_skip(tctx,
6364 : "block refcounting and compressed files not supported\n");
6365 : }
6366 :
6367 : /* set compressed flag on src */
6368 0 : status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
6369 : COMPRESSION_FORMAT_DEFAULT);
6370 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6371 :
6372 0 : ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
6373 0 : torture_assert(tctx, ok, "write pattern");
6374 :
6375 : /* extend dest */
6376 0 : ZERO_STRUCT(sinfo);
6377 0 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6378 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
6379 0 : sinfo.end_of_file_info.in.size = 4096;
6380 0 : status = smb2_setinfo_file(tree, &sinfo);
6381 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6382 :
6383 : /* copy all src file data */
6384 0 : dup_ext_buf.source_off = 0;
6385 0 : dup_ext_buf.target_off = 0;
6386 0 : dup_ext_buf.byte_count = 4096;
6387 :
6388 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6389 : &dup_ext_buf,
6390 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6391 0 : torture_assert_ndr_success(tctx, ndr_ret,
6392 : "ndr_push_fsctl_dup_extents_to_file");
6393 :
6394 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6395 0 : torture_assert_ntstatus_ok(tctx, status,
6396 : "FSCTL_DUP_EXTENTS_TO_FILE");
6397 :
6398 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6399 0 : if (!ok) {
6400 0 : torture_fail(tctx, "inconsistent file data");
6401 : }
6402 :
6403 0 : smb2_util_close(tree, src_h);
6404 0 : smb2_util_close(tree, dest_h);
6405 0 : talloc_free(tmp_ctx);
6406 0 : return true;
6407 : }
6408 :
6409 7 : static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
6410 : struct smb2_tree *tree)
6411 : {
6412 : struct smb2_handle src_h;
6413 : struct smb2_handle dest_h;
6414 : NTSTATUS status;
6415 : union smb_ioctl ioctl;
6416 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6417 : struct fsctl_dup_extents_to_file dup_ext_buf;
6418 : enum ndr_err_code ndr_ret;
6419 : union smb_setfileinfo sinfo;
6420 : bool ok;
6421 :
6422 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6423 : &src_h, 4096,
6424 : SEC_RIGHTS_FILE_ALL,
6425 : &dest_h, 0,
6426 : SEC_RIGHTS_FILE_ALL,
6427 : &dup_ext_buf,
6428 : &ioctl);
6429 7 : if (!ok) {
6430 0 : torture_fail(tctx, "setup dup extents error");
6431 : }
6432 :
6433 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6434 : FILE_SUPPORTS_BLOCK_REFCOUNTING
6435 : | FILE_FILE_COMPRESSION, &ok);
6436 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6437 7 : if (!ok) {
6438 7 : smb2_util_close(tree, src_h);
6439 7 : smb2_util_close(tree, dest_h);
6440 7 : talloc_free(tmp_ctx);
6441 7 : torture_skip(tctx,
6442 : "block refcounting and compressed files not supported\n");
6443 : }
6444 :
6445 : /* set compressed flag on dest */
6446 0 : status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
6447 : COMPRESSION_FORMAT_DEFAULT);
6448 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
6449 :
6450 : /* extend dest */
6451 0 : ZERO_STRUCT(sinfo);
6452 0 : sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6453 0 : sinfo.end_of_file_info.in.file.handle = dest_h;
6454 0 : sinfo.end_of_file_info.in.size = 4096;
6455 0 : status = smb2_setinfo_file(tree, &sinfo);
6456 0 : torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
6457 :
6458 : /* copy all src file data */
6459 0 : dup_ext_buf.source_off = 0;
6460 0 : dup_ext_buf.target_off = 0;
6461 0 : dup_ext_buf.byte_count = 4096;
6462 :
6463 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6464 : &dup_ext_buf,
6465 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6466 0 : torture_assert_ndr_success(tctx, ndr_ret,
6467 : "ndr_push_fsctl_dup_extents_to_file");
6468 :
6469 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6470 0 : torture_assert_ntstatus_ok(tctx, status,
6471 : "FSCTL_DUP_EXTENTS_TO_FILE");
6472 :
6473 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
6474 0 : if (!ok) {
6475 0 : torture_fail(tctx, "inconsistent file data");
6476 : }
6477 :
6478 0 : smb2_util_close(tree, src_h);
6479 0 : smb2_util_close(tree, dest_h);
6480 0 : talloc_free(tmp_ctx);
6481 0 : return true;
6482 : }
6483 :
6484 7 : static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
6485 : struct smb2_tree *tree)
6486 : {
6487 : struct smb2_handle src_h;
6488 : struct smb2_handle dest_h;
6489 : struct smb2_handle bogus_h;
6490 : NTSTATUS status;
6491 : union smb_ioctl ioctl;
6492 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6493 : struct fsctl_dup_extents_to_file dup_ext_buf;
6494 : enum ndr_err_code ndr_ret;
6495 : bool ok;
6496 :
6497 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6498 : &src_h, 32768, /* fill 32768 byte src file */
6499 : SEC_RIGHTS_FILE_ALL,
6500 : &dest_h, 32768,
6501 : SEC_RIGHTS_FILE_ALL,
6502 : &dup_ext_buf,
6503 : &ioctl);
6504 7 : if (!ok) {
6505 0 : torture_fail(tctx, "setup dup extents error");
6506 : }
6507 :
6508 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6509 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6510 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6511 7 : if (!ok) {
6512 7 : smb2_util_close(tree, src_h);
6513 7 : smb2_util_close(tree, dest_h);
6514 7 : talloc_free(tmp_ctx);
6515 7 : torture_skip(tctx, "block refcounting not supported\n");
6516 : }
6517 :
6518 : /* open and close a file, keeping the handle as now a "bogus" handle */
6519 0 : ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
6520 : &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
6521 : FILE_ATTRIBUTE_NORMAL);
6522 0 : torture_assert(tctx, ok, "bogus file create fill");
6523 0 : smb2_util_close(tree, bogus_h);
6524 :
6525 : /* bogus dest file handle */
6526 0 : ioctl.smb2.in.file.handle = bogus_h;
6527 :
6528 0 : dup_ext_buf.source_off = 0;
6529 0 : dup_ext_buf.target_off = 0;
6530 0 : dup_ext_buf.byte_count = 32768;
6531 :
6532 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6533 : &dup_ext_buf,
6534 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6535 0 : torture_assert_ndr_success(tctx, ndr_ret,
6536 : "ndr_push_fsctl_dup_extents_to_file");
6537 :
6538 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6539 0 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
6540 : "FSCTL_DUP_EXTENTS_TO_FILE");
6541 :
6542 0 : ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6543 0 : if (!ok) {
6544 0 : torture_fail(tctx, "inconsistent file data");
6545 : }
6546 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6547 0 : if (!ok) {
6548 0 : torture_fail(tctx, "inconsistent file data");
6549 : }
6550 :
6551 : /* reinstate dest, add bogus src file handle */
6552 0 : ioctl.smb2.in.file.handle = dest_h;
6553 0 : smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
6554 :
6555 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6556 : &dup_ext_buf,
6557 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6558 0 : torture_assert_ndr_success(tctx, ndr_ret,
6559 : "ndr_push_fsctl_dup_extents_to_file");
6560 :
6561 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6562 0 : torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
6563 : "FSCTL_DUP_EXTENTS_TO_FILE");
6564 :
6565 0 : ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
6566 0 : if (!ok) {
6567 0 : torture_fail(tctx, "inconsistent file data");
6568 : }
6569 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6570 0 : if (!ok) {
6571 0 : torture_fail(tctx, "inconsistent file data");
6572 : }
6573 :
6574 0 : smb2_util_close(tree, src_h);
6575 0 : smb2_util_close(tree, dest_h);
6576 0 : talloc_free(tmp_ctx);
6577 0 : return true;
6578 : }
6579 :
6580 7 : static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
6581 : struct smb2_tree *tree)
6582 : {
6583 : struct smb2_handle src_h;
6584 : struct smb2_handle src_h2;
6585 : struct smb2_handle dest_h;
6586 : NTSTATUS status;
6587 : union smb_ioctl ioctl;
6588 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6589 : struct fsctl_dup_extents_to_file dup_ext_buf;
6590 : enum ndr_err_code ndr_ret;
6591 : bool ok;
6592 : struct smb2_lock lck;
6593 : struct smb2_lock_element el[1];
6594 :
6595 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6596 : &src_h, 32768, /* fill 32768 byte src file */
6597 : SEC_RIGHTS_FILE_ALL,
6598 : &dest_h, 0,
6599 : SEC_RIGHTS_FILE_ALL,
6600 : &dup_ext_buf,
6601 : &ioctl);
6602 7 : if (!ok) {
6603 0 : torture_fail(tctx, "setup dup extents error");
6604 : }
6605 :
6606 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6607 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6608 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6609 7 : if (!ok) {
6610 7 : smb2_util_close(tree, src_h);
6611 7 : smb2_util_close(tree, dest_h);
6612 7 : talloc_free(tmp_ctx);
6613 7 : torture_skip(tctx, "block refcounting not supported\n");
6614 : }
6615 :
6616 : /* dest pattern is different to src */
6617 0 : ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6618 0 : torture_assert(tctx, ok, "write pattern");
6619 :
6620 : /* setup dup ext req, values used for locking */
6621 0 : dup_ext_buf.source_off = 0;
6622 0 : dup_ext_buf.target_off = 0;
6623 0 : dup_ext_buf.byte_count = 32768;
6624 :
6625 : /* open and lock the dup extents src file */
6626 0 : status = torture_smb2_testfile(tree, FNAME, &src_h2);
6627 0 : torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6628 :
6629 0 : lck.in.lock_count = 0x0001;
6630 0 : lck.in.lock_sequence = 0x00000000;
6631 0 : lck.in.file.handle = src_h2;
6632 0 : lck.in.locks = el;
6633 0 : el[0].offset = dup_ext_buf.source_off;
6634 0 : el[0].length = dup_ext_buf.byte_count;
6635 0 : el[0].reserved = 0;
6636 0 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6637 :
6638 0 : status = smb2_lock(tree, &lck);
6639 0 : torture_assert_ntstatus_ok(tctx, status, "lock");
6640 :
6641 0 : status = smb2_util_write(tree, src_h,
6642 : "conflicted", 0, sizeof("conflicted"));
6643 0 : torture_assert_ntstatus_equal(tctx, status,
6644 : NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6645 :
6646 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6647 : &dup_ext_buf,
6648 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6649 0 : torture_assert_ndr_success(tctx, ndr_ret,
6650 : "ndr_push_fsctl_dup_extents_to_file");
6651 :
6652 : /*
6653 : * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6654 : * here.
6655 : */
6656 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6657 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6658 :
6659 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6660 0 : if (!ok) {
6661 0 : torture_fail(tctx, "inconsistent file data");
6662 : }
6663 :
6664 0 : lck.in.lock_count = 0x0001;
6665 0 : lck.in.lock_sequence = 0x00000001;
6666 0 : lck.in.file.handle = src_h2;
6667 0 : lck.in.locks = el;
6668 0 : el[0].offset = dup_ext_buf.source_off;
6669 0 : el[0].length = dup_ext_buf.byte_count;
6670 0 : el[0].reserved = 0;
6671 0 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6672 0 : status = smb2_lock(tree, &lck);
6673 0 : torture_assert_ntstatus_ok(tctx, status, "unlock");
6674 :
6675 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6676 0 : torture_assert_ntstatus_ok(tctx, status,
6677 : "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6678 :
6679 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6680 0 : if (!ok) {
6681 0 : torture_fail(tctx, "inconsistent file data");
6682 : }
6683 :
6684 0 : smb2_util_close(tree, src_h2);
6685 0 : smb2_util_close(tree, src_h);
6686 0 : smb2_util_close(tree, dest_h);
6687 0 : talloc_free(tmp_ctx);
6688 0 : return true;
6689 : }
6690 :
6691 7 : static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6692 : struct smb2_tree *tree)
6693 : {
6694 : struct smb2_handle src_h;
6695 : struct smb2_handle dest_h;
6696 : struct smb2_handle dest_h2;
6697 : NTSTATUS status;
6698 : union smb_ioctl ioctl;
6699 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6700 : struct fsctl_dup_extents_to_file dup_ext_buf;
6701 : enum ndr_err_code ndr_ret;
6702 : bool ok;
6703 : struct smb2_lock lck;
6704 : struct smb2_lock_element el[1];
6705 :
6706 7 : ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6707 : &src_h, 32768, /* fill 32768 byte src file */
6708 : SEC_RIGHTS_FILE_ALL,
6709 : &dest_h, 0,
6710 : SEC_RIGHTS_FILE_ALL,
6711 : &dup_ext_buf,
6712 : &ioctl);
6713 7 : if (!ok) {
6714 0 : torture_fail(tctx, "setup dup extents error");
6715 : }
6716 :
6717 7 : status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6718 : FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6719 7 : torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6720 7 : if (!ok) {
6721 7 : smb2_util_close(tree, src_h);
6722 7 : smb2_util_close(tree, dest_h);
6723 7 : talloc_free(tmp_ctx);
6724 7 : torture_skip(tctx, "block refcounting not supported\n");
6725 : }
6726 :
6727 : /* dest pattern is different to src */
6728 0 : ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6729 0 : torture_assert(tctx, ok, "write pattern");
6730 :
6731 : /* setup dup ext req, values used for locking */
6732 0 : dup_ext_buf.source_off = 0;
6733 0 : dup_ext_buf.target_off = 0;
6734 0 : dup_ext_buf.byte_count = 32768;
6735 :
6736 : /* open and lock the dup extents dest file */
6737 0 : status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6738 0 : torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6739 :
6740 0 : lck.in.lock_count = 0x0001;
6741 0 : lck.in.lock_sequence = 0x00000000;
6742 0 : lck.in.file.handle = dest_h2;
6743 0 : lck.in.locks = el;
6744 0 : el[0].offset = dup_ext_buf.source_off;
6745 0 : el[0].length = dup_ext_buf.byte_count;
6746 0 : el[0].reserved = 0;
6747 0 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
6748 :
6749 0 : status = smb2_lock(tree, &lck);
6750 0 : torture_assert_ntstatus_ok(tctx, status, "lock");
6751 :
6752 0 : status = smb2_util_write(tree, dest_h,
6753 : "conflicted", 0, sizeof("conflicted"));
6754 0 : torture_assert_ntstatus_equal(tctx, status,
6755 : NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6756 :
6757 0 : ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6758 : &dup_ext_buf,
6759 : (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6760 0 : torture_assert_ndr_success(tctx, ndr_ret,
6761 : "ndr_push_fsctl_dup_extents_to_file");
6762 :
6763 : /*
6764 : * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6765 : * here.
6766 : */
6767 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6768 0 : torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6769 :
6770 0 : lck.in.lock_count = 0x0001;
6771 0 : lck.in.lock_sequence = 0x00000001;
6772 0 : lck.in.file.handle = dest_h2;
6773 0 : lck.in.locks = el;
6774 0 : el[0].offset = dup_ext_buf.source_off;
6775 0 : el[0].length = dup_ext_buf.byte_count;
6776 0 : el[0].reserved = 0;
6777 0 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
6778 0 : status = smb2_lock(tree, &lck);
6779 0 : torture_assert_ntstatus_ok(tctx, status, "unlock");
6780 :
6781 0 : status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6782 0 : torture_assert_ntstatus_ok(tctx, status,
6783 : "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6784 :
6785 0 : ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6786 0 : if (!ok) {
6787 0 : torture_fail(tctx, "inconsistent file data");
6788 : }
6789 :
6790 0 : smb2_util_close(tree, src_h);
6791 0 : smb2_util_close(tree, dest_h);
6792 0 : smb2_util_close(tree, dest_h2);
6793 0 : talloc_free(tmp_ctx);
6794 0 : return true;
6795 : }
6796 :
6797 : /*
6798 : basic regression test for BUG 14607
6799 : https://bugzilla.samba.org/show_bug.cgi?id=14607
6800 : */
6801 7 : static bool test_ioctl_bug14607(struct torture_context *torture,
6802 : struct smb2_tree *tree)
6803 : {
6804 7 : TALLOC_CTX *tmp_ctx = talloc_new(tree);
6805 : uint32_t timeout_msec;
6806 : NTSTATUS status;
6807 7 : DATA_BLOB out_input_buffer = data_blob_null;
6808 7 : DATA_BLOB out_output_buffer = data_blob_null;
6809 :
6810 7 : timeout_msec = tree->session->transport->options.request_timeout * 1000;
6811 :
6812 13 : status = smb2cli_ioctl(tree->session->transport->conn,
6813 : timeout_msec,
6814 7 : tree->session->smbXcli,
6815 : tree->smbXcli,
6816 : UINT64_MAX, /* in_fid_persistent */
6817 : UINT64_MAX, /* in_fid_volatile */
6818 : FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8,
6819 : 0, /* in_max_input_length */
6820 : NULL, /* in_input_buffer */
6821 : 1, /* in_max_output_length */
6822 : NULL, /* in_output_buffer */
6823 : SMB2_IOCTL_FLAG_IS_FSCTL,
6824 : tmp_ctx,
6825 : &out_input_buffer,
6826 : &out_output_buffer);
6827 13 : if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
6828 13 : NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) ||
6829 13 : NT_STATUS_EQUAL(status, NT_STATUS_FS_DRIVER_REQUIRED) ||
6830 7 : NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST))
6831 : {
6832 1 : torture_comment(torture,
6833 : "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8: %s\n",
6834 : nt_errstr(status));
6835 1 : torture_skip(torture, "server doesn't support FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8\n");
6836 : }
6837 6 : torture_assert_ntstatus_ok(torture, status, "FSCTL_SMBTORTURE_IOCTL_RESPONSE_BODY_PADDING8");
6838 :
6839 6 : torture_assert_int_equal(torture, out_output_buffer.length, 1,
6840 : "output length");
6841 6 : torture_assert_int_equal(torture, out_output_buffer.data[0], 8,
6842 : "output buffer byte should be 8");
6843 :
6844 6 : talloc_free(tmp_ctx);
6845 6 : return true;
6846 : }
6847 :
6848 : /*
6849 : basic regression test for BUG 14769
6850 : https://bugzilla.samba.org/show_bug.cgi?id=14769
6851 : */
6852 7 : static bool test_ioctl_bug14769(struct torture_context *torture,
6853 : struct smb2_tree *tree)
6854 : {
6855 : NTSTATUS status;
6856 7 : const char *fname = "bug14769";
6857 7 : bool ret = false;
6858 : struct smb2_handle h;
6859 : struct smb2_ioctl ioctl;
6860 : struct smb2_close cl;
6861 7 : struct smb2_request *smb2arr[2] = { 0 };
6862 7 : uint8_t tosend_msec = 200;
6863 7 : DATA_BLOB send_buf = { &tosend_msec, 1 };
6864 :
6865 : /* Create a test file. */
6866 7 : smb2_util_unlink(tree, fname);
6867 7 : status = torture_smb2_testfile(tree, fname, &h);
6868 7 : torture_assert_ntstatus_ok(torture, status, "create bug14769");
6869 :
6870 : /*
6871 : * Send (not receive) the FSCTL.
6872 : * This should go async with a wait time of 200 msec.
6873 : */
6874 7 : ZERO_STRUCT(ioctl);
6875 7 : ioctl.in.file.handle = h;
6876 7 : ioctl.in.function = FSCTL_SMBTORTURE_FSP_ASYNC_SLEEP;
6877 7 : ioctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
6878 7 : ioctl.in.out = send_buf;
6879 :
6880 7 : smb2arr[0] = smb2_ioctl_send(tree, &ioctl);
6881 7 : torture_assert_goto(torture,
6882 : smb2arr[0] != NULL,
6883 : ret,
6884 : done,
6885 : "smb2_ioctl_send failed\n");
6886 : /* Immediately send the close. */
6887 7 : ZERO_STRUCT(cl);
6888 7 : cl.in.file.handle = h;
6889 7 : cl.in.flags = 0;
6890 7 : smb2arr[1] = smb2_close_send(tree, &cl);
6891 7 : torture_assert_goto(torture,
6892 : smb2arr[1] != NULL,
6893 : ret,
6894 : done,
6895 : "smb2_close_send failed\n");
6896 :
6897 : /* Now get the FSCTL reply. */
6898 : /*
6899 : * If we suffer from bug #14769 this will fail as
6900 : * the ioctl will return with NT_STATUS_FILE_CLOSED,
6901 : * as the close will have closed the handle without
6902 : * waiting for the ioctl to complete. The server shouldn't
6903 : * complete the close until the ioctl finishes.
6904 : */
6905 7 : status = smb2_ioctl_recv(smb2arr[0], tree, &ioctl);
6906 7 : torture_assert_ntstatus_ok_goto(torture,
6907 : status,
6908 : ret,
6909 : done,
6910 : "smb2_ioctl_recv failed\n");
6911 :
6912 : /* Followed by the close reply. */
6913 6 : status = smb2_close_recv(smb2arr[1], &cl);
6914 6 : torture_assert_ntstatus_ok_goto(torture,
6915 : status,
6916 : ret,
6917 : done,
6918 : "smb2_ioctl_close failed\n");
6919 6 : ret = true;
6920 :
6921 7 : done:
6922 7 : smb2_util_unlink(tree, fname);
6923 7 : return ret;
6924 : }
6925 :
6926 : /*
6927 : * testing of SMB2 ioctls
6928 : */
6929 2355 : struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
6930 : {
6931 2355 : struct torture_suite *suite = torture_suite_create(ctx, "ioctl");
6932 :
6933 2355 : torture_suite_add_1smb2_test(suite, "shadow_copy",
6934 : test_ioctl_get_shadow_copy);
6935 2355 : torture_suite_add_1smb2_test(suite, "req_resume_key",
6936 : test_ioctl_req_resume_key);
6937 2355 : torture_suite_add_1smb2_test(suite, "req_two_resume_keys",
6938 : test_ioctl_req_two_resume_keys);
6939 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6940 : test_ioctl_copy_chunk_simple);
6941 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6942 : test_ioctl_copy_chunk_multi);
6943 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6944 : test_ioctl_copy_chunk_tiny);
6945 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6946 : test_ioctl_copy_chunk_over);
6947 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6948 : test_ioctl_copy_chunk_append);
6949 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6950 : test_ioctl_copy_chunk_limits);
6951 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6952 : test_ioctl_copy_chunk_src_lck);
6953 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6954 : test_ioctl_copy_chunk_dest_lck);
6955 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6956 : test_ioctl_copy_chunk_bad_key);
6957 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6958 : test_ioctl_copy_chunk_src_is_dest);
6959 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6960 : test_ioctl_copy_chunk_src_is_dest_overlap);
6961 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6962 : test_ioctl_copy_chunk_bad_access);
6963 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6964 : test_ioctl_copy_chunk_write_access);
6965 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6966 : test_ioctl_copy_chunk_src_exceed);
6967 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6968 : test_ioctl_copy_chunk_src_exceed_multi);
6969 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6970 : test_ioctl_copy_chunk_sparse_dest);
6971 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6972 : test_ioctl_copy_chunk_max_output_sz);
6973 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6974 : test_ioctl_copy_chunk_zero_length);
6975 2355 : torture_suite_add_1smb2_test(suite, "copy-chunk streams",
6976 : test_copy_chunk_streams);
6977 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares",
6978 : test_copy_chunk_across_shares);
6979 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares2",
6980 : test_copy_chunk_across_shares2);
6981 2355 : torture_suite_add_1smb2_test(suite, "copy_chunk_across_shares3",
6982 : test_copy_chunk_across_shares3);
6983 2355 : torture_suite_add_1smb2_test(suite, "compress_file_flag",
6984 : test_ioctl_compress_file_flag);
6985 2355 : torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6986 : test_ioctl_compress_dir_inherit);
6987 2355 : torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6988 : test_ioctl_compress_invalid_format);
6989 2355 : torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6990 : test_ioctl_compress_invalid_buf);
6991 2355 : torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6992 : test_ioctl_compress_query_file_attr);
6993 2355 : torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6994 : test_ioctl_compress_create_with_attr);
6995 2355 : torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6996 : test_ioctl_compress_inherit_disable);
6997 2355 : torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6998 : test_ioctl_compress_set_file_attr);
6999 2355 : torture_suite_add_1smb2_test(suite, "compress_perms",
7000 : test_ioctl_compress_perms);
7001 2355 : torture_suite_add_1smb2_test(suite, "compress_notsup_get",
7002 : test_ioctl_compress_notsup_get);
7003 2355 : torture_suite_add_1smb2_test(suite, "compress_notsup_set",
7004 : test_ioctl_compress_notsup_set);
7005 2355 : torture_suite_add_1smb2_test(suite, "network_interface_info",
7006 : test_ioctl_network_interface_info);
7007 2355 : torture_suite_add_1smb2_test(suite, "sparse_file_flag",
7008 : test_ioctl_sparse_file_flag);
7009 2355 : torture_suite_add_1smb2_test(suite, "sparse_file_attr",
7010 : test_ioctl_sparse_file_attr);
7011 2355 : torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
7012 : test_ioctl_sparse_dir_flag);
7013 2355 : torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
7014 : test_ioctl_sparse_set_nobuf);
7015 2355 : torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
7016 : test_ioctl_sparse_set_oversize);
7017 2355 : torture_suite_add_1smb2_test(suite, "sparse_qar",
7018 : test_ioctl_sparse_qar);
7019 2355 : torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
7020 : test_ioctl_sparse_qar_malformed);
7021 2355 : torture_suite_add_1smb2_test(suite, "sparse_punch",
7022 : test_ioctl_sparse_punch);
7023 2355 : torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
7024 : test_ioctl_sparse_hole_dealloc);
7025 2355 : torture_suite_add_1smb2_test(suite, "sparse_compressed",
7026 : test_ioctl_sparse_compressed);
7027 2355 : torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
7028 : test_ioctl_sparse_copy_chunk);
7029 2355 : torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
7030 : test_ioctl_sparse_punch_invalid);
7031 2355 : torture_suite_add_1smb2_test(suite, "sparse_perms",
7032 : test_ioctl_sparse_perms);
7033 2355 : torture_suite_add_1smb2_test(suite, "sparse_lock",
7034 : test_ioctl_sparse_lck);
7035 2355 : torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
7036 : test_ioctl_sparse_qar_ob1);
7037 2355 : torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
7038 : test_ioctl_sparse_qar_multi);
7039 2355 : torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
7040 : test_ioctl_sparse_qar_overflow);
7041 2355 : torture_suite_add_1smb2_test(suite, "trim_simple",
7042 : test_ioctl_trim_simple);
7043 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_simple",
7044 : test_ioctl_dup_extents_simple);
7045 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
7046 : test_ioctl_dup_extents_len_beyond_dest);
7047 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
7048 : test_ioctl_dup_extents_len_beyond_src);
7049 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
7050 : test_ioctl_dup_extents_len_zero);
7051 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
7052 : test_ioctl_dup_extents_sparse_src);
7053 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
7054 : test_ioctl_dup_extents_sparse_dest);
7055 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
7056 : test_ioctl_dup_extents_sparse_both);
7057 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
7058 : test_ioctl_dup_extents_src_is_dest);
7059 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
7060 : test_ioctl_dup_extents_src_is_dest_overlap);
7061 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
7062 : test_ioctl_dup_extents_compressed_src);
7063 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
7064 : test_ioctl_dup_extents_compressed_dest);
7065 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
7066 : test_ioctl_dup_extents_bad_handle);
7067 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
7068 : test_ioctl_dup_extents_src_lck);
7069 2355 : torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
7070 : test_ioctl_dup_extents_dest_lck);
7071 2355 : torture_suite_add_1smb2_test(suite, "bug14607",
7072 : test_ioctl_bug14607);
7073 2355 : torture_suite_add_1smb2_test(suite, "bug14769",
7074 : test_ioctl_bug14769);
7075 :
7076 2355 : suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
7077 :
7078 2355 : return suite;
7079 : }
7080 :
|