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