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