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