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