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