torture/ioctl: switch sparse src/dest dup ext behaviour
[metze/samba/wip.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 /*
2547    basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2548 */
2549 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2550                                       struct smb2_tree *tree)
2551 {
2552         union smb_ioctl ioctl;
2553         struct smb2_handle fh;
2554         NTSTATUS status;
2555         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2556         struct fsctl_net_iface_info net_iface;
2557         enum ndr_err_code ndr_ret;
2558         uint32_t caps;
2559
2560         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2561         if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2562                 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2563         }
2564
2565         ZERO_STRUCT(ioctl);
2566         ioctl.smb2.level = RAW_IOCTL_SMB2;
2567         fh.data[0] = UINT64_MAX;
2568         fh.data[1] = UINT64_MAX;
2569         ioctl.smb2.in.file.handle = fh;
2570         ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2571         ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2572         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2573
2574         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2575         torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2576
2577         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2578                         (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2579         torture_assert_ndr_success(torture, ndr_ret,
2580                                    "ndr_pull_fsctl_net_iface_info");
2581
2582         ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2583                         "Network Interface Info", &net_iface);
2584
2585         talloc_free(tmp_ctx);
2586         return true;
2587 }
2588
2589 /*
2590  * Check whether all @fs_support_flags are set in the server's
2591  * RAW_QFS_ATTRIBUTE_INFORMATION FileSystemAttributes response.
2592  */
2593 static NTSTATUS test_ioctl_fs_supported(struct torture_context *torture,
2594                                         struct smb2_tree *tree,
2595                                         TALLOC_CTX *mem_ctx,
2596                                         struct smb2_handle *fh,
2597                                         uint64_t fs_support_flags,
2598                                         bool *supported)
2599 {
2600         NTSTATUS status;
2601         union smb_fsinfo info;
2602
2603         ZERO_STRUCT(info);
2604         info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2605         info.generic.handle = *fh;
2606         status = smb2_getinfo_fs(tree, tree, &info);
2607         if (!NT_STATUS_IS_OK(status)) {
2608                 return status;
2609         }
2610
2611         if ((info.attribute_info.out.fs_attr & fs_support_flags)
2612                                                         == fs_support_flags) {
2613                 *supported = true;
2614         } else {
2615                 *supported = false;
2616         }
2617         return NT_STATUS_OK;
2618 }
2619
2620 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2621                                       TALLOC_CTX *mem_ctx,
2622                                       struct smb2_tree *tree,
2623                                       struct smb2_handle fh,
2624                                       bool set)
2625 {
2626         union smb_ioctl ioctl;
2627         NTSTATUS status;
2628         uint8_t set_sparse;
2629
2630         ZERO_STRUCT(ioctl);
2631         ioctl.smb2.level = RAW_IOCTL_SMB2;
2632         ioctl.smb2.in.file.handle = fh;
2633         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2634         ioctl.smb2.in.max_response_size = 0;
2635         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2636         set_sparse = (set ? 0xFF : 0x0);
2637         ioctl.smb2.in.out.data = &set_sparse;
2638         ioctl.smb2.in.out.length = sizeof(set_sparse);
2639
2640         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2641         return status;
2642 }
2643
2644 static NTSTATUS test_sparse_get(struct torture_context *torture,
2645                                 TALLOC_CTX *mem_ctx,
2646                                 struct smb2_tree *tree,
2647                                 struct smb2_handle fh,
2648                                 bool *_is_sparse)
2649 {
2650         union smb_fileinfo io;
2651         NTSTATUS status;
2652
2653         ZERO_STRUCT(io);
2654         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2655         io.generic.in.file.handle = fh;
2656         status = smb2_getinfo_file(tree, mem_ctx, &io);
2657         if (!NT_STATUS_IS_OK(status)) {
2658                 return status;
2659         }
2660         *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2661
2662         return status;
2663 }
2664
2665 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2666                                         struct smb2_tree *tree)
2667 {
2668         struct smb2_handle fh;
2669         union smb_fileinfo io;
2670         NTSTATUS status;
2671         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2672         bool ok;
2673         bool is_sparse;
2674
2675         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2676                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2677                                     FILE_ATTRIBUTE_NORMAL);
2678         torture_assert(torture, ok, "setup file");
2679
2680         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2681                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
2682         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2683         if (!ok) {
2684                 smb2_util_close(tree, fh);
2685                 torture_skip(torture, "Sparse files not supported\n");
2686         }
2687
2688         ZERO_STRUCT(io);
2689         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2690         io.generic.in.file.handle = fh;
2691         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2692         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2693
2694         torture_assert(torture,
2695                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2696                        "sparse attr before set");
2697
2698         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2699         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2700
2701         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2702         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2703         torture_assert(torture, is_sparse, "no sparse attr after set");
2704
2705         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2706         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2707
2708         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2709         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2710         torture_assert(torture, !is_sparse, "sparse attr after unset");
2711
2712         smb2_util_close(tree, fh);
2713         talloc_free(tmp_ctx);
2714         return true;
2715 }
2716
2717 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2718                                         struct smb2_tree *tree)
2719 {
2720         struct smb2_handle fh;
2721         NTSTATUS status;
2722         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2723         bool ok;
2724         bool is_sparse;
2725
2726         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2727                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2728                         (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2729         torture_assert(torture, ok, "setup file");
2730
2731         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2732                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
2733         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2734         if (!ok) {
2735                 smb2_util_close(tree, fh);
2736                 torture_skip(torture, "Sparse files not supported\n");
2737         }
2738
2739         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2740         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2741         torture_assert(torture, !is_sparse, "sparse attr on open");
2742
2743         smb2_util_close(tree, fh);
2744         talloc_free(tmp_ctx);
2745         return true;
2746 }
2747
2748 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2749                                         struct smb2_tree *tree)
2750 {
2751         struct smb2_handle dirh;
2752         NTSTATUS status;
2753         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2754         bool ok;
2755
2756         smb2_deltree(tree, DNAME);
2757         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2758                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2759                                     FILE_ATTRIBUTE_DIRECTORY);
2760         torture_assert(torture, ok, "setup sparse directory");
2761
2762         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
2763                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
2764         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2765         if (!ok) {
2766                 smb2_util_close(tree, dirh);
2767                 smb2_deltree(tree, DNAME);
2768                 torture_skip(torture, "Sparse files not supported\n");
2769         }
2770
2771         /* set sparse dir should fail, check for 2k12 & 2k8 response */
2772         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2773         torture_assert_ntstatus_equal(torture, status,
2774                                       NT_STATUS_INVALID_PARAMETER,
2775                                       "dir FSCTL_SET_SPARSE status");
2776
2777         smb2_util_close(tree, dirh);
2778         smb2_deltree(tree, DNAME);
2779         talloc_free(tmp_ctx);
2780         return true;
2781 }
2782
2783 /*
2784  * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2785  * buffer to indicate whether the flag should be set or cleared. When sent
2786  * without a buffer, it must be handled as if SetSparse=TRUE.
2787  */
2788 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2789                                         struct smb2_tree *tree)
2790 {
2791         struct smb2_handle fh;
2792         union smb_ioctl ioctl;
2793         NTSTATUS status;
2794         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2795         bool ok;
2796         bool is_sparse;
2797
2798         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2799                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2800                                     FILE_ATTRIBUTE_NORMAL);
2801         torture_assert(torture, ok, "setup file");
2802
2803         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2804                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
2805         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2806         if (!ok) {
2807                 smb2_util_close(tree, fh);
2808                 torture_skip(torture, "Sparse files not supported\n");
2809         }
2810
2811         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2812         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2813         torture_assert(torture, !is_sparse, "sparse attr before set");
2814
2815         ZERO_STRUCT(ioctl);
2816         ioctl.smb2.level = RAW_IOCTL_SMB2;
2817         ioctl.smb2.in.file.handle = fh;
2818         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2819         ioctl.smb2.in.max_response_size = 0;
2820         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2821         /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2822
2823         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2824         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2825
2826         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2827         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2828         torture_assert(torture, is_sparse, "no sparse attr after set");
2829
2830         /* second non-SetSparse request shouldn't toggle sparse */
2831         ZERO_STRUCT(ioctl);
2832         ioctl.smb2.level = RAW_IOCTL_SMB2;
2833         ioctl.smb2.in.file.handle = fh;
2834         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2835         ioctl.smb2.in.max_response_size = 0;
2836         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2837
2838         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2839         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2840
2841         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2842         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2843         torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2844
2845         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2846         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2847
2848         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2849         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2850         torture_assert(torture, !is_sparse, "sparse attr after unset");
2851
2852         smb2_util_close(tree, fh);
2853         talloc_free(tmp_ctx);
2854         return true;
2855 }
2856
2857 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2858                                            struct smb2_tree *tree)
2859 {
2860         struct smb2_handle fh;
2861         union smb_ioctl ioctl;
2862         NTSTATUS status;
2863         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2864         bool ok;
2865         bool is_sparse;
2866         uint8_t buf[100];
2867
2868         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2869                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2870                                     FILE_ATTRIBUTE_NORMAL);
2871         torture_assert(torture, ok, "setup file");
2872
2873         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
2874                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
2875         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2876         if (!ok) {
2877                 smb2_util_close(tree, fh);
2878                 torture_skip(torture, "Sparse files not supported\n");
2879         }
2880
2881         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2882         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2883         torture_assert(torture, !is_sparse, "sparse attr before set");
2884
2885         ZERO_STRUCT(ioctl);
2886         ioctl.smb2.level = RAW_IOCTL_SMB2;
2887         ioctl.smb2.in.file.handle = fh;
2888         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2889         ioctl.smb2.in.max_response_size = 0;
2890         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2891
2892         /*
2893          * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2894          * Windows still successfully processes the request.
2895          */
2896         ZERO_ARRAY(buf);
2897         buf[0] = 0xFF; /* attempt to set sparse */
2898         ioctl.smb2.in.out.data = buf;
2899         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2900
2901         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2902         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2903
2904         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2905         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2906         torture_assert(torture, is_sparse, "no sparse attr after set");
2907
2908         ZERO_STRUCT(ioctl);
2909         ioctl.smb2.level = RAW_IOCTL_SMB2;
2910         ioctl.smb2.in.file.handle = fh;
2911         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2912         ioctl.smb2.in.max_response_size = 0;
2913         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2914
2915         ZERO_ARRAY(buf); /* clear sparse */
2916         ioctl.smb2.in.out.data = buf;
2917         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
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, "sparse attr after clear");
2925
2926         smb2_util_close(tree, fh);
2927         talloc_free(tmp_ctx);
2928         return true;
2929 }
2930
2931 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
2932                                    TALLOC_CTX *mem_ctx,
2933                                    struct smb2_tree *tree,
2934                                    struct smb2_handle fh,
2935                                    int64_t req_off,
2936                                    int64_t req_len,
2937                                    struct file_alloced_range_buf **_rsp,
2938                                    uint64_t *_rsp_count)
2939 {
2940         union smb_ioctl ioctl;
2941         NTSTATUS status;
2942         enum ndr_err_code ndr_ret;
2943         struct file_alloced_range_buf far_buf;
2944         struct file_alloced_range_buf *far_rsp = NULL;
2945         uint64_t far_count = 0;
2946         int i;
2947         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2948         if (tmp_ctx == NULL) {
2949                 return NT_STATUS_NO_MEMORY;
2950         }
2951
2952         ZERO_STRUCT(ioctl);
2953         ioctl.smb2.level = RAW_IOCTL_SMB2;
2954         ioctl.smb2.in.file.handle = fh;
2955         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
2956         ioctl.smb2.in.max_response_size = 1024;
2957         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2958
2959         far_buf.file_off = req_off;
2960         far_buf.len = req_len;
2961
2962         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
2963                                        &far_buf,
2964                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
2965         if (ndr_ret != NDR_ERR_SUCCESS) {
2966                 status = NT_STATUS_UNSUCCESSFUL;
2967                 goto err_out;
2968         }
2969
2970         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2971         if (!NT_STATUS_IS_OK(status)) {
2972                 goto err_out;
2973         }
2974
2975         if (ioctl.smb2.out.out.length == 0) {
2976                 goto done;
2977         }
2978
2979         if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
2980                 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
2981                                 ioctl.smb2.out.out.length);
2982                 status = NT_STATUS_INVALID_VIEW_SIZE;
2983                 goto err_out;
2984         }
2985
2986         far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
2987         far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
2988                                far_count);
2989         if (far_rsp == NULL) {
2990                 status = NT_STATUS_NO_MEMORY;
2991                 goto err_out;
2992         }
2993
2994         for (i = 0; i < far_count; i++) {
2995                 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
2996                                                &far_rsp[i],
2997                         (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
2998                 if (ndr_ret != NDR_ERR_SUCCESS) {
2999                         status = NT_STATUS_UNSUCCESSFUL;
3000                         goto err_out;
3001                 }
3002                 /* move to next buffer */
3003                 ioctl.smb2.out.out.data += sizeof(far_buf);
3004                 ioctl.smb2.out.out.length -= sizeof(far_buf);
3005         }
3006
3007 done:
3008         *_rsp = far_rsp;
3009         *_rsp_count = far_count;
3010         status = NT_STATUS_OK;
3011 err_out:
3012         talloc_free(tmp_ctx);
3013         return status;
3014 }
3015
3016 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3017                                   struct smb2_tree *tree)
3018 {
3019         struct smb2_handle fh;
3020         NTSTATUS status;
3021         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3022         bool ok;
3023         bool is_sparse;
3024         struct file_alloced_range_buf *far_rsp = NULL;
3025         uint64_t far_count = 0;
3026
3027         /* zero length file, shouldn't have any ranges */
3028         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3029                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3030                                     FILE_ATTRIBUTE_NORMAL);
3031         torture_assert(torture, ok, "setup file");
3032
3033         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3034                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3035         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3036         if (!ok) {
3037                 smb2_util_close(tree, fh);
3038                 torture_skip(torture, "Sparse files not supported\n");
3039         }
3040
3041         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3042         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3043         torture_assert(torture, !is_sparse, "sparse attr before set");
3044
3045         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3046                                     0,  /* off */
3047                                     0,  /* len */
3048                                     &far_rsp,
3049                                     &far_count);
3050         torture_assert_ntstatus_ok(torture, status,
3051                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3052         torture_assert_u64_equal(torture, far_count, 0,
3053                                  "unexpected response len");
3054
3055         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3056                                     0,  /* off */
3057                                     1024,       /* len */
3058                                     &far_rsp,
3059                                     &far_count);
3060         torture_assert_ntstatus_ok(torture, status,
3061                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3062         torture_assert_u64_equal(torture, far_count, 0,
3063                                  "unexpected response len");
3064
3065         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3066         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3067
3068         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3069         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3070         torture_assert(torture, is_sparse, "no sparse attr after set");
3071
3072         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3073                                     0,  /* off */
3074                                     1024,       /* len */
3075                                     &far_rsp,
3076                                     &far_count);
3077         torture_assert_ntstatus_ok(torture, status,
3078                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3079         torture_assert_u64_equal(torture, far_count, 0,
3080                                  "unexpected response len");
3081
3082         /* write into the (now) sparse file at 4k offset */
3083         ok = write_pattern(torture, tree, tmp_ctx, fh,
3084                            4096,        /* off */
3085                            1024,        /* len */
3086                            4096);       /* pattern offset */
3087         torture_assert(torture, ok, "write pattern");
3088
3089         /*
3090          * Query range before write off. Whether it's allocated or not is FS
3091          * dependent. NTFS deallocates chunks in 64K increments, but others
3092          * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3093          */
3094         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3095                                     0,  /* off */
3096                                     4096,       /* len */
3097                                     &far_rsp,
3098                                     &far_count);
3099         torture_assert_ntstatus_ok(torture, status,
3100                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3101         if (far_count == 0) {
3102                 torture_comment(torture, "FS deallocated 4K chunk\n");
3103         } else {
3104                 /* expect fully allocated */
3105                 torture_assert_u64_equal(torture, far_count, 1,
3106                                          "unexpected response len");
3107                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3108                 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3109         }
3110
3111         /*
3112          * Query range before and past write, it should be allocated up to the
3113          * end of the write.
3114          */
3115         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3116                                     0,  /* off */
3117                                     8192,       /* len */
3118                                     &far_rsp,
3119                                     &far_count);
3120         torture_assert_ntstatus_ok(torture, status,
3121                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3122         torture_assert_u64_equal(torture, far_count, 1,
3123                                  "unexpected response len");
3124         /* FS dependent */
3125         if (far_rsp[0].file_off == 4096) {
3126                 /* 4K chunk unallocated */
3127                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3128                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3129         } else {
3130                 /* expect fully allocated */
3131                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3132                 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3133         }
3134
3135         smb2_util_close(tree, fh);
3136         talloc_free(tmp_ctx);
3137         return true;
3138 }
3139
3140 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3141                                             struct smb2_tree *tree)
3142 {
3143         struct smb2_handle fh;
3144         union smb_ioctl ioctl;
3145         struct file_alloced_range_buf far_buf;
3146         NTSTATUS status;
3147         enum ndr_err_code ndr_ret;
3148         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3149         bool ok;
3150         size_t old_len;
3151
3152         /* zero length file, shouldn't have any ranges */
3153         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3154                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3155                                     FILE_ATTRIBUTE_NORMAL);
3156         torture_assert(torture, ok, "setup file");
3157
3158         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3159                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3160         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3161         if (!ok) {
3162                 smb2_util_close(tree, fh);
3163                 torture_skip(torture, "Sparse files not supported\n");
3164         }
3165
3166         /* no allocated ranges, no space for range response, should pass */
3167         ZERO_STRUCT(ioctl);
3168         ioctl.smb2.level = RAW_IOCTL_SMB2;
3169         ioctl.smb2.in.file.handle = fh;
3170         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3171         ioctl.smb2.in.max_response_size = 0;
3172         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3173
3174         far_buf.file_off = 0;
3175         far_buf.len = 1024;
3176         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3177                                        &far_buf,
3178                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3179         torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3180
3181         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3182         torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3183
3184         /* write into the file at 4k offset */
3185         ok = write_pattern(torture, tree, tmp_ctx, fh,
3186                            0,           /* off */
3187                            1024,        /* len */
3188                            0);          /* pattern offset */
3189         torture_assert(torture, ok, "write pattern");
3190
3191         /* allocated range, no space for range response, should fail */
3192         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3193         torture_assert_ntstatus_equal(torture, status,
3194                                       NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3195
3196         /* oversize (2x) file_alloced_range_buf in request, should pass */
3197         ioctl.smb2.in.max_response_size = 1024;
3198         old_len = ioctl.smb2.in.out.length;
3199         ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3200                                (ioctl.smb2.in.out.length * 2));
3201         torture_assert(torture, ok, "2x data buffer");
3202         memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3203                old_len);
3204         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3205         torture_assert_ntstatus_ok(torture, status, "qar too big");
3206
3207         /* no file_alloced_range_buf in request, should fail */
3208         data_blob_free(&ioctl.smb2.in.out);
3209         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3210         torture_assert_ntstatus_equal(torture, status,
3211                                       NT_STATUS_INVALID_PARAMETER, "qar empty");
3212
3213         return true;
3214 }
3215
3216 /*
3217  * 2.3.57 FSCTL_SET_ZERO_DATA Request
3218  *
3219  * How an implementation zeros data within a file is implementation-dependent.
3220  * A file system MAY choose to deallocate regions of disk space that have been
3221  * zeroed.<50>
3222  * <50>
3223  * ... NTFS might deallocate disk space in the file if the file is stored on an
3224  * NTFS volume, and the file is sparse or compressed. It will free any allocated
3225  * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3226  * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3227  * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3228  * deallocated.
3229  */
3230 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3231                                      TALLOC_CTX *mem_ctx,
3232                                      struct smb2_tree *tree,
3233                                      struct smb2_handle fh,
3234                                      int64_t off,
3235                                      int64_t beyond_final_zero)
3236 {
3237         union smb_ioctl ioctl;
3238         NTSTATUS status;
3239         enum ndr_err_code ndr_ret;
3240         struct file_zero_data_info zdata_info;
3241         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3242         if (tmp_ctx == NULL) {
3243                 return NT_STATUS_NO_MEMORY;
3244         }
3245
3246         ZERO_STRUCT(ioctl);
3247         ioctl.smb2.level = RAW_IOCTL_SMB2;
3248         ioctl.smb2.in.file.handle = fh;
3249         ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3250         ioctl.smb2.in.max_response_size = 0;
3251         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3252
3253         zdata_info.file_off = off;
3254         zdata_info.beyond_final_zero = beyond_final_zero;
3255
3256         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3257                                        &zdata_info,
3258                         (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3259         if (ndr_ret != NDR_ERR_SUCCESS) {
3260                 status = NT_STATUS_UNSUCCESSFUL;
3261                 goto err_out;
3262         }
3263
3264         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3265         if (!NT_STATUS_IS_OK(status)) {
3266                 goto err_out;
3267         }
3268
3269         status = NT_STATUS_OK;
3270 err_out:
3271         talloc_free(tmp_ctx);
3272         return status;
3273 }
3274
3275 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3276                                     struct smb2_tree *tree)
3277 {
3278         struct smb2_handle fh;
3279         NTSTATUS status;
3280         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3281         bool ok;
3282         bool is_sparse;
3283         struct file_alloced_range_buf *far_rsp = NULL;
3284         uint64_t far_count = 0;
3285
3286         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3287                                     FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3288                                     FILE_ATTRIBUTE_NORMAL);
3289         torture_assert(torture, ok, "setup file");
3290
3291         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3292                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3293         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3294         if (!ok) {
3295                 smb2_util_close(tree, fh);
3296                 torture_skip(torture, "Sparse files not supported\n");
3297         }
3298
3299         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3300         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3301         torture_assert(torture, !is_sparse, "sparse attr before set");
3302
3303         /* zero (hole-punch) the data, without sparse flag */
3304         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3305                                       0,        /* off */
3306                                       4096);    /* beyond_final_zero */
3307         torture_assert_ntstatus_ok(torture, status, "zero_data");
3308
3309         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3310                                     0,          /* off */
3311                                     4096,       /* len */
3312                                     &far_rsp,
3313                                     &far_count);
3314         torture_assert_ntstatus_ok(torture, status,
3315                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3316         torture_assert_u64_equal(torture, far_count, 1,
3317                                  "unexpected response len");
3318
3319         /* expect fully allocated */
3320         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3321                                  "unexpected far off");
3322         torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3323                                  "unexpected far len");
3324         /* check that the data is now zeroed */
3325         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3326         torture_assert(torture, ok, "non-sparse zeroed range");
3327
3328         /* set sparse */
3329         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3330         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3331
3332         /* still fully allocated on NTFS, see note below for Samba */
3333         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3334                                     0,          /* off */
3335                                     4096,       /* len */
3336                                     &far_rsp,
3337                                     &far_count);
3338         torture_assert_ntstatus_ok(torture, status,
3339                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3340         /*
3341          * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3342          * subsequently uses fallocate() to allocate the punched range if the
3343          * file is marked non-sparse and "strict allocate" is enabled. In both
3344          * cases, the zeroed range will not be detected by SEEK_DATA, so the
3345          * range won't be present in QAR responses until the file is marked
3346          * non-sparse again.
3347          */
3348         if (far_count == 0) {
3349                 torture_comment(torture, "non-sparse zeroed range disappeared "
3350                                 "after marking sparse\n");
3351         } else {
3352                 /* NTFS: range remains fully allocated */
3353                 torture_assert_u64_equal(torture, far_count, 1,
3354                                          "unexpected response len");
3355                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3356                                          "unexpected far off");
3357                 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3358                                          "unexpected far len");
3359         }
3360
3361         /* zero (hole-punch) the data, _with_ sparse flag */
3362         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3363                                       0,        /* off */
3364                                       4096);    /* beyond_final_zero */
3365         torture_assert_ntstatus_ok(torture, status, "zero_data");
3366
3367         /* the range should no longer be alloced */
3368         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3369                                     0,          /* off */
3370                                     4096,       /* len */
3371                                     &far_rsp,
3372                                     &far_count);
3373         torture_assert_ntstatus_ok(torture, status,
3374                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3375         torture_assert_u64_equal(torture, far_count, 0,
3376                                  "unexpected response len");
3377
3378         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3379         torture_assert(torture, ok, "sparse zeroed range");
3380
3381         /* remove sparse flag, this should "unsparse" the zeroed range */
3382         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3383         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3384
3385         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3386                                     0,          /* off */
3387                                     4096,       /* len */
3388                                     &far_rsp,
3389                                     &far_count);
3390         torture_assert_ntstatus_ok(torture, status,
3391                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3392         torture_assert_u64_equal(torture, far_count, 1,
3393                                  "unexpected response len");
3394         /* expect fully allocated */
3395         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3396                                  "unexpected far off");
3397         torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3398                                  "unexpected far len");
3399
3400         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3401         torture_assert(torture, ok, "sparse zeroed range");
3402
3403         smb2_util_close(tree, fh);
3404         talloc_free(tmp_ctx);
3405         return true;
3406 }
3407
3408 /*
3409  * Find the point at which a zeroed range in a sparse file is deallocated by the
3410  * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3411  * increments. Also check whether zeroed neighbours are merged for deallocation.
3412  */
3413 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3414                                            struct smb2_tree *tree)
3415 {
3416         struct smb2_handle fh;
3417         NTSTATUS status;
3418         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3419         bool ok;
3420         uint64_t file_size;
3421         uint64_t hlen;
3422         uint64_t dealloc_chunk_len = 0;
3423         struct file_alloced_range_buf *far_rsp = NULL;
3424         uint64_t far_count = 0;
3425
3426         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3427                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3428                                     FILE_ATTRIBUTE_NORMAL);
3429         torture_assert(torture, ok, "setup file 1");
3430
3431         /* check for FS sparse file */
3432         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3433                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3434         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3435         if (!ok) {
3436                 smb2_util_close(tree, fh);
3437                 torture_skip(torture, "Sparse files not supported\n");
3438         }
3439
3440         /* set sparse */
3441         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3442         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3443
3444         file_size = 1024 * 1024;
3445
3446         ok = write_pattern(torture, tree, tmp_ctx, fh,
3447                            0,           /* off */
3448                            file_size,   /* len */
3449                            0);  /* pattern offset */
3450         torture_assert(torture, ok, "write pattern");
3451
3452          /* check allocated ranges, should be fully allocated */
3453         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3454                                     0,                  /* off */
3455                                     file_size,          /* len */
3456                                     &far_rsp,
3457                                     &far_count);
3458         torture_assert_ntstatus_ok(torture, status,
3459                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3460         torture_assert_u64_equal(torture, far_count, 1,
3461                                  "unexpected response len");
3462         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3463                                  "unexpected far off");
3464         torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3465                                  "unexpected far len");
3466
3467         /* punch holes in sizes of 1k increments */
3468         for (hlen = 0; hlen <= file_size; hlen += 4096) {
3469
3470                 /* punch a hole from zero to the current increment */
3471                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3472                                               0,        /* off */
3473                                               hlen);    /* beyond_final_zero */
3474                 torture_assert_ntstatus_ok(torture, status, "zero_data");
3475
3476                 /* ensure hole is zeroed, and pattern is consistent */
3477                 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3478                 torture_assert(torture, ok, "sparse zeroed range");
3479
3480                 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3481                                    file_size - hlen, hlen);
3482                 torture_assert(torture, ok, "allocated pattern range");
3483
3484                  /* Check allocated ranges, hole might have been deallocated */
3485                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3486                                             0,          /* off */
3487                                             file_size,  /* len */
3488                                             &far_rsp,
3489                                             &far_count);
3490                 torture_assert_ntstatus_ok(torture, status,
3491                                            "FSCTL_QUERY_ALLOCATED_RANGES");
3492                 if ((hlen == file_size) && (far_count == 0)) {
3493                         /* hole covered entire file, deallocation occurred */
3494                         dealloc_chunk_len = file_size;
3495                         break;
3496                 }
3497
3498                 torture_assert_u64_equal(torture, far_count, 1,
3499                                          "unexpected response len");
3500                 if (far_rsp[0].file_off != 0) {
3501                         /*
3502                          * We now know the hole punch length needed to trigger a
3503                          * deallocation on this FS...
3504                          */
3505                         dealloc_chunk_len = hlen;
3506                         torture_comment(torture, "hole punch %ju@0 resulted in "
3507                                         "deallocation of %ju@0\n",
3508                                         (uintmax_t)hlen,
3509                                         (uintmax_t)far_rsp[0].file_off);
3510                         torture_assert_u64_equal(torture,
3511                                                  file_size - far_rsp[0].len,
3512                                                  far_rsp[0].file_off,
3513                                                  "invalid alloced range");
3514                         break;
3515                 }
3516         }
3517
3518         if (dealloc_chunk_len == 0) {
3519                 torture_comment(torture, "strange, this FS never deallocates"
3520                                 "zeroed ranges in sparse files\n");
3521                 return true;    /* FS specific, not a failure */
3522         }
3523
3524         /*
3525          * Check whether deallocation occurs when the (now known)
3526          * deallocation chunk size is punched via two ZERO_DATA requests.
3527          * I.e. Does the FS merge the two ranges and deallocate the chunk?
3528          * NTFS on Windows Server 2012 does not.
3529          */
3530         ok = write_pattern(torture, tree, tmp_ctx, fh,
3531                            0,           /* off */
3532                            file_size,   /* len */
3533                            0);  /* pattern offset */
3534         torture_assert(torture, ok, "write pattern");
3535
3536         /* divide dealloc chunk size by two, to use as punch length */
3537         hlen = dealloc_chunk_len >> 1;
3538
3539         /*
3540          *                     /half of dealloc chunk size           1M\
3541          *                     |                                       |
3542          * /offset 0           |                   /dealloc chunk size |
3543          * |------------------ |-------------------|-------------------|
3544          * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern  |
3545          */
3546         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3547                                       0,        /* off */
3548                                       hlen);    /* beyond final zero */
3549         torture_assert_ntstatus_ok(torture, status, "zero_data");
3550
3551         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3552                                       hlen,     /* off */
3553                                       dealloc_chunk_len); /* beyond final */
3554         torture_assert_ntstatus_ok(torture, status, "zero_data");
3555
3556         /* ensure holes are zeroed, and pattern is consistent */
3557         ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3558         torture_assert(torture, ok, "sparse zeroed range");
3559
3560         ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3561                            file_size - dealloc_chunk_len, dealloc_chunk_len);
3562         torture_assert(torture, ok, "allocated pattern range");
3563
3564         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3565                                     0,                  /* off */
3566                                     file_size,          /* len */
3567                                     &far_rsp,
3568                                     &far_count);
3569         torture_assert_ntstatus_ok(torture, status,
3570                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3571
3572         if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3573                 torture_comment(torture, "holes merged for deallocation of "
3574                                 "full file\n");
3575                 return true;
3576         }
3577         torture_assert_u64_equal(torture, far_count, 1,
3578                                  "unexpected response len");
3579         if (far_rsp[0].file_off == dealloc_chunk_len) {
3580                 torture_comment(torture, "holes merged for deallocation of "
3581                                 "%ju chunk\n", (uintmax_t)dealloc_chunk_len);
3582                 torture_assert_u64_equal(torture,
3583                                          file_size - far_rsp[0].len,
3584                                          far_rsp[0].file_off,
3585                                          "invalid alloced range");
3586         } else {
3587                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3588                                          "unexpected deallocation");
3589                 torture_comment(torture, "holes not merged for deallocation\n");
3590         }
3591
3592         smb2_util_close(tree, fh);
3593
3594         /*
3595          * Check whether an unwritten range is allocated when a sparse file is
3596          * written to at an offset past the dealloc chunk size:
3597          *
3598          *                     /dealloc chunk size
3599          * /offset 0           |
3600          * |------------------ |-------------------|
3601          * |     unwritten     |      pattern      |
3602          */
3603         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3604                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3605                                     FILE_ATTRIBUTE_NORMAL);
3606         torture_assert(torture, ok, "setup file 1");
3607
3608         /* set sparse */
3609         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3610         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3611
3612         ok = write_pattern(torture, tree, tmp_ctx, fh,
3613                            dealloc_chunk_len,   /* off */
3614                            1024,                /* len */
3615                            dealloc_chunk_len);  /* pattern offset */
3616         torture_assert(torture, ok, "write pattern");
3617
3618         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3619                                     0,                          /* off */
3620                                     dealloc_chunk_len + 1024,   /* len */
3621                                     &far_rsp,
3622                                     &far_count);
3623         torture_assert_ntstatus_ok(torture, status,
3624                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3625         torture_assert_u64_equal(torture, far_count, 1,
3626                                  "unexpected response len");
3627         if (far_rsp[0].file_off == 0) {
3628                 torture_assert_u64_equal(torture, far_rsp[0].len,
3629                                          dealloc_chunk_len + 1024,
3630                                          "unexpected far len");
3631                 torture_comment(torture, "unwritten range fully allocated\n");
3632         } else {
3633                 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3634                                          "unexpected deallocation");
3635                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3636                                          "unexpected far len");
3637                 torture_comment(torture, "unwritten range not allocated\n");
3638         }
3639
3640         ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3641         torture_assert(torture, ok, "sparse zeroed range");
3642
3643         ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3644                            1024, dealloc_chunk_len);
3645         torture_assert(torture, ok, "allocated pattern range");
3646
3647         /* unsparse, should now be fully allocated */
3648         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3649         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3650
3651         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3652                                     0,                          /* off */
3653                                     dealloc_chunk_len + 1024,   /* len */
3654                                     &far_rsp,
3655                                     &far_count);
3656         torture_assert_ntstatus_ok(torture, status,
3657                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3658         torture_assert_u64_equal(torture, far_count, 1,
3659                                  "unexpected response len");
3660         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3661                                  "unexpected deallocation");
3662         torture_assert_u64_equal(torture, far_rsp[0].len,
3663                                  dealloc_chunk_len + 1024,
3664                                  "unexpected far len");
3665
3666         smb2_util_close(tree, fh);
3667         talloc_free(tmp_ctx);
3668         return true;
3669 }
3670
3671 /* check whether a file with compression and sparse attrs can be deallocated */
3672 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
3673                                          struct smb2_tree *tree)
3674 {
3675         struct smb2_handle fh;
3676         NTSTATUS status;
3677         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3678         bool ok;
3679         uint64_t file_size = 1024 * 1024;
3680         struct file_alloced_range_buf *far_rsp = NULL;
3681         uint64_t far_count = 0;
3682
3683         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3684                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3685                                     FILE_ATTRIBUTE_NORMAL);
3686         torture_assert(torture, ok, "setup file 1");
3687
3688         /* check for FS sparse file and compression support */
3689         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3690                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3691         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3692         if (!ok) {
3693                 smb2_util_close(tree, fh);
3694                 torture_skip(torture, "Sparse files not supported\n");
3695         }
3696
3697         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3698                                                   &ok);
3699         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3700         if (!ok) {
3701                 smb2_util_close(tree, fh);
3702                 torture_skip(torture, "FS compression not supported\n");
3703         }
3704
3705         /* set compression and write some data */
3706         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3707                                          COMPRESSION_FORMAT_DEFAULT);
3708         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
3709
3710         ok = write_pattern(torture, tree, tmp_ctx, fh,
3711                            0,           /* off */
3712                            file_size,   /* len */
3713                            0);          /* pattern offset */
3714         torture_assert(torture, ok, "write pattern");
3715
3716         /* set sparse - now sparse and compressed */
3717         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3718         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3719
3720          /* check allocated ranges, should be fully alloced */
3721         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3722                                     0,          /* off */
3723                                     file_size,  /* len */
3724                                     &far_rsp,
3725                                     &far_count);
3726         torture_assert_ntstatus_ok(torture, status,
3727                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3728         torture_assert_u64_equal(torture, far_count, 1,
3729                                  "unexpected response len");
3730         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3731                                  "unexpected far off");
3732         torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3733                                  "unexpected far len");
3734
3735         /* zero (hole-punch) all data, with sparse and compressed attrs */
3736         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3737                                       0,                /* off */
3738                                       file_size);       /* beyond_final_zero */
3739         torture_assert_ntstatus_ok(torture, status, "zero_data");
3740
3741          /*
3742           * Windows Server 2012 still deallocates a zeroed range when a sparse
3743           * file carries the compression attribute.
3744           */
3745         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3746                                     0,          /* off */
3747                                     file_size,  /* len */
3748                                     &far_rsp,
3749                                     &far_count);
3750         torture_assert_ntstatus_ok(torture, status,
3751                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3752         if (far_count == 0) {
3753                 torture_comment(torture, "sparse & compressed file "
3754                                 "deallocated after hole-punch\n");
3755         } else {
3756                 torture_assert_u64_equal(torture, far_count, 1,
3757                                          "unexpected response len");
3758                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3759                                          "unexpected far off");
3760                 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3761                                          "unexpected far len");
3762                 torture_comment(torture, "sparse & compressed file fully "
3763                                 "allocated after hole-punch\n");
3764         }
3765
3766         smb2_util_close(tree, fh);
3767         talloc_free(tmp_ctx);
3768         return true;
3769 }
3770
3771 /*
3772  * Create a sparse file, then attempt to copy unallocated and allocated ranges
3773  * into a target file using FSCTL_SRV_COPYCHUNK.
3774  */
3775 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
3776                                          struct smb2_tree *tree)
3777 {
3778         struct smb2_handle src_h;
3779         struct smb2_handle dest_h;
3780         NTSTATUS status;
3781         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3782         bool ok;
3783         uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
3784         struct file_alloced_range_buf *far_rsp = NULL;
3785         uint64_t far_count = 0;
3786         union smb_ioctl ioctl;
3787         struct srv_copychunk_copy cc_copy;
3788         struct srv_copychunk_rsp cc_rsp;
3789         enum ndr_err_code ndr_ret;
3790
3791         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3792                                     FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
3793                                     FILE_ATTRIBUTE_NORMAL);
3794         torture_assert(torture, ok, "setup file");
3795
3796         /* check for FS sparse file support */
3797         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &src_h,
3798                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3799         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3800         smb2_util_close(tree, src_h);
3801         if (!ok) {
3802                 torture_skip(torture, "Sparse files not supported\n");
3803         }
3804
3805         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
3806                                    1, /* chunks */
3807                                    &src_h, 0, /* src file */
3808                                    SEC_RIGHTS_FILE_ALL,
3809                                    &dest_h, 0,  /* dest file */
3810                                    SEC_RIGHTS_FILE_ALL,
3811                                    &cc_copy,
3812                                    &ioctl);
3813         torture_assert(torture, ok, "setup copy chunk error");
3814
3815         /* set sparse */
3816         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
3817         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3818
3819         /* start after dealloc_chunk_len, to create an unwritten sparse range */
3820         ok = write_pattern(torture, tree, tmp_ctx, src_h,
3821                            dealloc_chunk_len,   /* off */
3822                            1024,        /* len */
3823                            dealloc_chunk_len);  /* pattern offset */
3824         torture_assert(torture, ok, "write pattern");
3825
3826          /* Skip test if 64k chunk is allocated - FS specific */
3827         status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
3828                                     0,                          /* off */
3829                                     dealloc_chunk_len + 1024,   /* len */
3830                                     &far_rsp,
3831                                     &far_count);
3832         torture_assert_ntstatus_ok(torture, status,
3833                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3834         torture_assert_u64_equal(torture, far_count, 1,
3835                                  "unexpected response len");
3836         if (far_rsp[0].file_off == 0) {
3837                 torture_skip(torture, "unwritten range fully allocated\n");
3838         }
3839
3840         torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3841                                  "unexpected allocation");
3842         torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3843                                  "unexpected far len");
3844
3845         /* copy-chunk unallocated + written ranges into non-sparse dest */
3846
3847         cc_copy.chunks[0].source_off = 0;
3848         cc_copy.chunks[0].target_off = 0;
3849         cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
3850
3851         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3852                                        &cc_copy,
3853                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
3854         torture_assert_ndr_success(torture, ndr_ret,
3855                                    "ndr_push_srv_copychunk_copy");
3856
3857         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3858         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3859
3860         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3861                                        &cc_rsp,
3862                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3863         torture_assert_ndr_success(torture, ndr_ret,
3864                                    "ndr_pull_srv_copychunk_rsp");
3865
3866         ok = check_copy_chunk_rsp(torture, &cc_rsp,
3867                                   1,    /* chunks written */
3868                                   0,    /* chunk bytes unsuccessfully written */
3869                                   dealloc_chunk_len + 1024); /* bytes written */
3870         torture_assert(torture, ok, "bad copy chunk response data");
3871
3872         ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3873         torture_assert(torture, ok, "sparse zeroed range");
3874
3875         ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3876                            1024, dealloc_chunk_len);
3877         torture_assert(torture, ok, "copychunked range");
3878
3879         /* copied range should be allocated in non-sparse dest */
3880         status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3881                                     0,                          /* off */
3882                                     dealloc_chunk_len + 1024,   /* len */
3883                                     &far_rsp,
3884                                     &far_count);
3885         torture_assert_ntstatus_ok(torture, status,
3886                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3887         torture_assert_u64_equal(torture, far_count, 1,
3888                                  "unexpected response len");
3889         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3890                                  "unexpected allocation");
3891         torture_assert_u64_equal(torture, far_rsp[0].len,
3892                                  dealloc_chunk_len + 1024,
3893                                  "unexpected far len");
3894
3895         /* set dest as sparse */
3896         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
3897         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3898
3899         /* zero (hole-punch) all data */
3900         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
3901                                       0,                /* off */
3902                                       dealloc_chunk_len + 1024);
3903         torture_assert_ntstatus_ok(torture, status, "zero_data");
3904
3905         /* zeroed range might be deallocated */
3906         status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3907                                     0,                          /* off */
3908                                     dealloc_chunk_len + 1024,   /* len */
3909                                     &far_rsp,
3910                                     &far_count);
3911         torture_assert_ntstatus_ok(torture, status,
3912                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3913         if (far_count == 0) {
3914                 /* FS specific (e.g. NTFS) */
3915                 torture_comment(torture, "FS deallocates file on full-range "
3916                                 "punch\n");
3917         } else {
3918                 /* FS specific (e.g. EXT4) */
3919                 torture_comment(torture, "FS doesn't deallocate file on "
3920                                 "full-range punch\n");
3921         }
3922         ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
3923                         dealloc_chunk_len + 1024);
3924         torture_assert(torture, ok, "punched zeroed range");
3925
3926         /* copy-chunk again, this time with sparse dest */
3927         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3928         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3929
3930         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3931                                        &cc_rsp,
3932                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3933         torture_assert_ndr_success(torture, ndr_ret,
3934                                    "ndr_pull_srv_copychunk_rsp");
3935
3936         ok = check_copy_chunk_rsp(torture, &cc_rsp,
3937                                   1,    /* chunks written */
3938                                   0,    /* chunk bytes unsuccessfully written */
3939                                   dealloc_chunk_len + 1024); /* bytes written */
3940         torture_assert(torture, ok, "bad copy chunk response data");
3941
3942         ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3943         torture_assert(torture, ok, "sparse zeroed range");
3944
3945         ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3946                            1024, dealloc_chunk_len);
3947         torture_assert(torture, ok, "copychunked range");
3948
3949         /* copied range may be allocated in sparse dest */
3950         status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3951                                     0,                          /* off */
3952                                     dealloc_chunk_len + 1024,   /* len */
3953                                     &far_rsp,
3954                                     &far_count);
3955         torture_assert_ntstatus_ok(torture, status,
3956                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3957         torture_assert_u64_equal(torture, far_count, 1,
3958                                  "unexpected response len");
3959         /*
3960          * FS specific: sparse region may be unallocated in dest if copy-chunk
3961          *              is handled in a sparse preserving way - E.g. vfs_btrfs
3962          *              with BTRFS_IOC_CLONE_RANGE.
3963          */
3964         if (far_rsp[0].file_off == dealloc_chunk_len) {
3965                 torture_comment(torture, "copy-chunk sparse range preserved\n");
3966                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3967                                          "unexpected far len");
3968         } else {
3969                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3970                                          "unexpected allocation");
3971                 torture_assert_u64_equal(torture, far_rsp[0].len,
3972                                          dealloc_chunk_len + 1024,
3973                                          "unexpected far len");
3974         }
3975
3976         smb2_util_close(tree, src_h);
3977         smb2_util_close(tree, dest_h);
3978         talloc_free(tmp_ctx);
3979         return true;
3980 }
3981
3982 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
3983                                             struct smb2_tree *tree)
3984 {
3985         struct smb2_handle fh;
3986         NTSTATUS status;
3987         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3988         bool ok;
3989         bool is_sparse;
3990         int i;
3991
3992         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3993                                     FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3994                                     FILE_ATTRIBUTE_NORMAL);
3995         torture_assert(torture, ok, "setup file");
3996
3997         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3998                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3999         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4000         if (!ok) {
4001                 smb2_util_close(tree, fh);
4002                 torture_skip(torture, "Sparse files not supported\n");
4003         }
4004
4005         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4006         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4007         torture_assert(torture, !is_sparse, "sparse attr before set");
4008
4009         /* loop twice, without and with sparse attrib */
4010         for (i = 0; i <= 1;  i++) {
4011                 union smb_fileinfo io;
4012                 struct file_alloced_range_buf *far_rsp = NULL;
4013                 uint64_t far_count = 0;
4014
4015                 /* get size before & after. zero data should never change it */
4016                 ZERO_STRUCT(io);
4017                 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4018                 io.generic.in.file.handle = fh;
4019                 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4020                 torture_assert_ntstatus_ok(torture, status, "getinfo");
4021                 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4022                                          4096, "size after IO");
4023
4024                 /* valid 8 byte zero data, but after EOF */
4025                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4026                                               4096,     /* off */
4027                                               4104);    /* beyond_final_zero */
4028                 torture_assert_ntstatus_ok(torture, status, "zero_data");
4029
4030                 /* valid 8 byte zero data, but after EOF */
4031                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4032                                               8192,     /* off */
4033                                               8200);    /* beyond_final_zero */
4034                 torture_assert_ntstatus_ok(torture, status, "zero_data");
4035
4036                 ZERO_STRUCT(io);
4037                 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4038                 io.generic.in.file.handle = fh;
4039                 status = smb2_getinfo_file(tree, tmp_ctx, &io);
4040                 torture_assert_ntstatus_ok(torture, status, "getinfo");
4041                 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
4042                                          4096, "size after IO");
4043
4044                 /* valid 0 byte zero data, without sparse flag */
4045                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4046                                               4095,     /* off */
4047                                               4095);    /* beyond_final_zero */
4048                 torture_assert_ntstatus_ok(torture, status, "zero_data");
4049
4050                 /* INVALID off is past beyond_final_zero */
4051                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4052                                               4096,     /* off */
4053                                               4095);    /* beyond_final_zero */
4054                 torture_assert_ntstatus_equal(torture, status,
4055                                               NT_STATUS_INVALID_PARAMETER,
4056                                               "invalid zero_data");
4057
4058                 /* zero length QAR - valid */
4059                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4060                                             0,                  /* off */
4061                                             0,                  /* len */
4062                                             &far_rsp, &far_count);
4063                 torture_assert_ntstatus_ok(torture, status,
4064                                 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4065                 torture_assert_u64_equal(torture, far_count, 0,
4066                                          "unexpected response len");
4067
4068                 /* QAR after EOF - valid */
4069                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4070                                             4096,               /* off */
4071                                             1024,               /* len */
4072                                             &far_rsp, &far_count);
4073                 torture_assert_ntstatus_ok(torture, status,
4074                                 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4075                 torture_assert_u64_equal(torture, far_count, 0,
4076                                          "unexpected response len");
4077
4078                 /* set sparse */
4079                 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4080                                                true);
4081                 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4082         }
4083
4084         smb2_util_close(tree, fh);
4085         talloc_free(tmp_ctx);
4086         return true;
4087 }
4088
4089 static bool test_ioctl_sparse_perms(struct torture_context *torture,
4090                                     struct smb2_tree *tree)
4091 {
4092         struct smb2_handle fh;
4093         NTSTATUS status;
4094         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4095         bool ok;
4096         bool is_sparse;
4097         struct file_alloced_range_buf *far_rsp = NULL;
4098         uint64_t far_count = 0;
4099
4100         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4101                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4102                                     FILE_ATTRIBUTE_NORMAL);
4103         torture_assert(torture, ok, "setup file");
4104
4105         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4106                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
4107         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4108         smb2_util_close(tree, fh);
4109         if (!ok) {
4110                 torture_skip(torture, "Sparse files not supported\n");
4111         }
4112
4113         /* set sparse without WRITE_ATTR permission should succeed */
4114         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4115                                     FNAME, &fh, 0,
4116                         (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
4117                                                         | SEC_STD_WRITE_DAC
4118                                                         | SEC_FILE_WRITE_EA)),
4119                                     FILE_ATTRIBUTE_NORMAL);
4120         torture_assert(torture, ok, "setup file");
4121
4122         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4123         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4124         smb2_util_close(tree, fh);
4125
4126         ok = test_setup_open(torture, tree, tmp_ctx,
4127                              FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4128                              FILE_ATTRIBUTE_NORMAL);
4129         torture_assert(torture, ok, "setup file");
4130         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4131         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4132         torture_assert(torture, is_sparse, "sparse after set");
4133         smb2_util_close(tree, fh);
4134
4135         /* attempt get sparse without READ_DATA permission */
4136         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4137                                     FNAME, &fh, 0,
4138                         (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
4139                                     FILE_ATTRIBUTE_NORMAL);
4140         torture_assert(torture, ok, "setup file");
4141
4142         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4143         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4144         torture_assert(torture, !is_sparse, "sparse set");
4145         smb2_util_close(tree, fh);
4146
4147         /* attempt to set sparse with only WRITE_ATTR permission */
4148         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4149                                     FNAME, &fh, 0,
4150                                     SEC_FILE_WRITE_ATTRIBUTE,
4151                                     FILE_ATTRIBUTE_NORMAL);
4152         torture_assert(torture, ok, "setup file");
4153
4154         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4155         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4156         smb2_util_close(tree, fh);
4157
4158         /* attempt to set sparse with only WRITE_DATA permission */
4159         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4160                                     FNAME, &fh, 0,
4161                                     SEC_FILE_WRITE_DATA,
4162                                     FILE_ATTRIBUTE_NORMAL);
4163         torture_assert(torture, ok, "setup file");
4164
4165         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4166         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4167         smb2_util_close(tree, fh);
4168
4169         ok = test_setup_open(torture, tree, tmp_ctx,
4170                              FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4171                              FILE_ATTRIBUTE_NORMAL);
4172         torture_assert(torture, ok, "setup file");
4173         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4174         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4175         torture_assert(torture, is_sparse, "sparse after set");
4176         smb2_util_close(tree, fh);
4177
4178         /* attempt to set sparse with only APPEND_DATA permission */
4179         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4180                                     FNAME, &fh, 0,
4181                                     SEC_FILE_APPEND_DATA,
4182                                     FILE_ATTRIBUTE_NORMAL);
4183         torture_assert(torture, ok, "setup file");
4184
4185         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4186         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4187         smb2_util_close(tree, fh);
4188
4189         ok = test_setup_open(torture, tree, tmp_ctx,
4190                              FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4191                              FILE_ATTRIBUTE_NORMAL);
4192         torture_assert(torture, ok, "setup file");
4193         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4194         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4195         torture_assert(torture, is_sparse, "sparse after set");
4196         smb2_util_close(tree, fh);
4197
4198         /* attempt to set sparse with only WRITE_EA permission - should fail */
4199         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4200                                     FNAME, &fh, 0,
4201                                     SEC_FILE_WRITE_EA,
4202                                     FILE_ATTRIBUTE_NORMAL);
4203         torture_assert(torture, ok, "setup file");
4204
4205         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4206         torture_assert_ntstatus_equal(torture, status,
4207                                       NT_STATUS_ACCESS_DENIED,
4208                                       "FSCTL_SET_SPARSE permission");
4209         smb2_util_close(tree, fh);
4210
4211         ok = test_setup_open(torture, tree, tmp_ctx,
4212                              FNAME, &fh, SEC_RIGHTS_FILE_ALL,
4213                              FILE_ATTRIBUTE_NORMAL);
4214         torture_assert(torture, ok, "setup file");
4215         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4216         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4217         torture_assert(torture, !is_sparse, "sparse after set");
4218         smb2_util_close(tree, fh);
4219
4220         /* attempt QAR with only READ_ATTR permission - should fail */
4221         ok = test_setup_open(torture, tree, tmp_ctx,
4222                              FNAME, &fh, SEC_FILE_READ_ATTRIBUTE,
4223                              FILE_ATTRIBUTE_NORMAL);
4224         torture_assert(torture, ok, "setup file");
4225         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4226                                     4096,               /* off */
4227                                     1024,               /* len */
4228                                     &far_rsp, &far_count);
4229         torture_assert_ntstatus_equal(torture, status,
4230                                       NT_STATUS_ACCESS_DENIED,
4231                         "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4232         smb2_util_close(tree, fh);
4233
4234         /* attempt QAR with only READ_DATA permission */
4235         ok = test_setup_open(torture, tree, tmp_ctx,
4236                              FNAME, &fh, SEC_FILE_READ_DATA,
4237                              FILE_ATTRIBUTE_NORMAL);
4238         torture_assert(torture, ok, "setup file");
4239         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4240                                     0,          /* off */
4241                                     1024,               /* len */
4242                                     &far_rsp, &far_count);
4243         torture_assert_ntstatus_ok(torture, status,
4244                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4245         torture_assert_u64_equal(torture, far_count, 0,
4246                                  "unexpected response len");
4247         smb2_util_close(tree, fh);
4248
4249         /* attempt QAR with only READ_EA permission - should fail */
4250         ok = test_setup_open(torture, tree, tmp_ctx,
4251                              FNAME, &fh, SEC_FILE_READ_EA,
4252                              FILE_ATTRIBUTE_NORMAL);
4253         torture_assert(torture, ok, "setup file");
4254         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4255                                     4096,               /* off */
4256                                     1024,               /* len */
4257                                     &far_rsp, &far_count);
4258         torture_assert_ntstatus_equal(torture, status,
4259                                       NT_STATUS_ACCESS_DENIED,
4260                         "FSCTL_QUERY_ALLOCATED_RANGES req passed");
4261         smb2_util_close(tree, fh);
4262
4263         /* setup file for ZERO_DATA permissions tests */
4264         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4265                                     FNAME, &fh, 8192,
4266                                     SEC_RIGHTS_FILE_ALL,
4267                                     FILE_ATTRIBUTE_NORMAL);
4268         torture_assert(torture, ok, "setup file");
4269
4270         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4271         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4272         smb2_util_close(tree, fh);
4273
4274         /* attempt ZERO_DATA with only WRITE_ATTR permission - should fail */
4275         ok = test_setup_open(torture, tree, tmp_ctx,
4276                              FNAME, &fh, SEC_FILE_WRITE_ATTRIBUTE,
4277                              FILE_ATTRIBUTE_NORMAL);
4278         torture_assert(torture, ok, "setup file");
4279         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4280                                       0,        /* off */
4281                                       4096);    /* beyond_final_zero */
4282         torture_assert_ntstatus_equal(torture, status,
4283                                       NT_STATUS_ACCESS_DENIED,
4284                                       "zero_data permission");
4285         smb2_util_close(tree, fh);
4286
4287         /* attempt ZERO_DATA with only WRITE_DATA permission */
4288         ok = test_setup_open(torture, tree, tmp_ctx,
4289                              FNAME, &fh, SEC_FILE_WRITE_DATA,
4290                              FILE_ATTRIBUTE_NORMAL);
4291         torture_assert(torture, ok, "setup file");
4292         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4293                                       0,        /* off */
4294                                       4096);    /* beyond_final_zero */
4295         torture_assert_ntstatus_ok(torture, status, "zero_data");
4296         smb2_util_close(tree, fh);
4297
4298         /* attempt ZERO_DATA with only APPEND_DATA permission - should fail */
4299         ok = test_setup_open(torture, tree, tmp_ctx,
4300                              FNAME, &fh, SEC_FILE_APPEND_DATA,
4301                              FILE_ATTRIBUTE_NORMAL);
4302         torture_assert(torture, ok, "setup file");
4303         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4304                                       0,        /* off */
4305                                       4096);    /* beyond_final_zero */
4306         torture_assert_ntstatus_equal(torture, status,
4307                                       NT_STATUS_ACCESS_DENIED,
4308                                       "zero_data permission");
4309         smb2_util_close(tree, fh);
4310
4311         /* attempt ZERO_DATA with only WRITE_EA permission - should fail */
4312         ok = test_setup_open(torture, tree, tmp_ctx,
4313                              FNAME, &fh, SEC_FILE_WRITE_EA,
4314                              FILE_ATTRIBUTE_NORMAL);
4315         torture_assert(torture, ok, "setup file");
4316         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4317                                       0,        /* off */
4318                                       4096);    /* beyond_final_zero */
4319         torture_assert_ntstatus_equal(torture, status,
4320                                       NT_STATUS_ACCESS_DENIED,
4321                                       "zero_data permission");
4322         smb2_util_close(tree, fh);
4323
4324         talloc_free(tmp_ctx);
4325         return true;
4326 }
4327
4328 static bool test_ioctl_sparse_lck(struct torture_context *torture,
4329                                   struct smb2_tree *tree)
4330 {
4331         struct smb2_handle fh;
4332         struct smb2_handle fh2;
4333         NTSTATUS status;
4334         uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4335         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4336         bool ok;
4337         bool is_sparse;
4338         struct smb2_lock lck;
4339         struct smb2_lock_element el[1];
4340         struct file_alloced_range_buf *far_rsp = NULL;
4341         uint64_t far_count = 0;
4342
4343         ok = test_setup_create_fill(torture, tree, tmp_ctx, FNAME, &fh,
4344                                     dealloc_chunk_len, SEC_RIGHTS_FILE_ALL,
4345                                     FILE_ATTRIBUTE_NORMAL);
4346         torture_assert(torture, ok, "setup file");
4347
4348         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4349                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
4350         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4351         if (!ok) {
4352                 torture_skip(torture, "Sparse files not supported\n");
4353                 smb2_util_close(tree, fh);
4354         }
4355
4356         /* open and lock via separate fh2 */
4357         status = torture_smb2_testfile(tree, FNAME, &fh2);
4358         torture_assert_ntstatus_ok(torture, status, "2nd src open");
4359
4360         lck.in.lock_count       = 0x0001;
4361         lck.in.lock_sequence    = 0x00000000;
4362         lck.in.file.handle      = fh2;
4363         lck.in.locks            = el;
4364         el[0].offset            = 0;
4365         el[0].length            = dealloc_chunk_len;
4366         el[0].reserved          = 0;
4367         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
4368
4369         status = smb2_lock(tree, &lck);
4370         torture_assert_ntstatus_ok(torture, status, "lock");
4371
4372         /* set sparse while locked */
4373         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4374         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4375
4376         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
4377         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
4378         torture_assert(torture, is_sparse, "sparse attr after set");
4379
4380         /* zero data over locked range should fail */
4381         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4382                                       0,        /* off */
4383                                       4096);    /* beyond_final_zero */
4384         torture_assert_ntstatus_equal(torture, status,
4385                                       NT_STATUS_FILE_LOCK_CONFLICT,
4386                                       "zero_data locked");
4387
4388         /* QAR over locked range should pass */
4389         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4390                                     0,          /* off */
4391                                     4096,       /* len */
4392                                     &far_rsp, &far_count);
4393         torture_assert_ntstatus_ok(torture, status,
4394                         "FSCTL_QUERY_ALLOCATED_RANGES locked");
4395         torture_assert_u64_equal(torture, far_count, 1,
4396                                  "unexpected response len");
4397         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4398                                  "unexpected allocation");
4399         torture_assert_u64_equal(torture, far_rsp[0].len,
4400                                  4096,
4401                                  "unexpected far len");
4402
4403         /* zero data over range past EOF should pass */
4404         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4405                                       dealloc_chunk_len,        /* off */
4406                                       dealloc_chunk_len + 4096);
4407         torture_assert_ntstatus_ok(torture, status,
4408                                    "zero_data past EOF locked");
4409
4410         /* QAR over range past EOF should pass */
4411         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4412                                     dealloc_chunk_len,          /* off */
4413                                     4096,                       /* len */
4414                                     &far_rsp, &far_count);
4415         torture_assert_ntstatus_ok(torture, status,
4416                         "FSCTL_QUERY_ALLOCATED_RANGES past EOF locked");
4417         torture_assert_u64_equal(torture, far_count, 0,
4418                                  "unexpected response len");
4419
4420         lck.in.lock_count       = 0x0001;
4421         lck.in.lock_sequence    = 0x00000001;
4422         lck.in.file.handle      = fh2;
4423         lck.in.locks            = el;
4424         el[0].offset            = 0;
4425         el[0].length            = dealloc_chunk_len;
4426         el[0].reserved          = 0;
4427         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
4428         status = smb2_lock(tree, &lck);
4429         torture_assert_ntstatus_ok(torture, status, "unlock");
4430
4431         smb2_util_close(tree, fh2);
4432         smb2_util_close(tree, fh);
4433         talloc_free(tmp_ctx);
4434         return true;
4435 }
4436
4437 /* alleviate QAR off-by-one bug paranoia - help me ob1 */
4438 static bool test_ioctl_sparse_qar_ob1(struct torture_context *torture,
4439                                       struct smb2_tree *tree)
4440 {
4441         struct smb2_handle fh;
4442         NTSTATUS status;
4443         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4444         bool ok;
4445         uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4446         struct file_alloced_range_buf *far_rsp = NULL;
4447         uint64_t far_count = 0;
4448
4449         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4450                                     FNAME, &fh, dealloc_chunk_len * 2,
4451                                     SEC_RIGHTS_FILE_ALL,
4452                                     FILE_ATTRIBUTE_NORMAL);
4453         torture_assert(torture, ok, "setup file");
4454
4455         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4456                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
4457         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4458         if (!ok) {
4459                 torture_skip(torture, "Sparse files not supported\n");
4460                 smb2_util_close(tree, fh);
4461         }
4462
4463         /* non-sparse QAR with range one before EOF */
4464         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4465                                     0,                          /* off */
4466                                     dealloc_chunk_len * 2 - 1,  /* len */
4467                                     &far_rsp, &far_count);
4468         torture_assert_ntstatus_ok(torture, status,
4469                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4470         torture_assert_u64_equal(torture, far_count, 1,
4471                                  "unexpected response len");
4472         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4473                                  "unexpected allocation");
4474         torture_assert_u64_equal(torture, far_rsp[0].len,
4475                                  dealloc_chunk_len * 2 - 1,
4476                                  "unexpected far len");
4477
4478         /* non-sparse QAR with range one after EOF */
4479         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4480                                     0,                          /* off */
4481                                     dealloc_chunk_len * 2 + 1,  /* len */
4482                                     &far_rsp, &far_count);
4483         torture_assert_ntstatus_ok(torture, status,
4484                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4485         torture_assert_u64_equal(torture, far_count, 1,
4486                                  "unexpected response len");
4487         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4488                                  "unexpected allocation");
4489         torture_assert_u64_equal(torture, far_rsp[0].len,
4490                                  dealloc_chunk_len * 2,
4491                                  "unexpected far len");
4492
4493         /* non-sparse QAR with range one after EOF from off=1 */
4494         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4495                                     1,                          /* off */
4496                                     dealloc_chunk_len * 2,      /* len */
4497                                     &far_rsp, &far_count);
4498         torture_assert_ntstatus_ok(torture, status,
4499                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4500         torture_assert_u64_equal(torture, far_count, 1,
4501                                  "unexpected response len");
4502         torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4503                                  "unexpected allocation");
4504         torture_assert_u64_equal(torture, far_rsp[0].len,
4505                                  dealloc_chunk_len * 2 - 1,
4506                                  "unexpected far len");
4507
4508         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4509         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4510
4511         /* punch out second chunk */
4512         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4513                                       dealloc_chunk_len,        /* off */
4514                                       dealloc_chunk_len * 2);
4515         torture_assert_ntstatus_ok(torture, status, "zero_data");
4516
4517         /* sparse QAR with range one before hole */
4518         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4519                                     0,                          /* off */
4520                                     dealloc_chunk_len - 1,      /* len */
4521                                     &far_rsp, &far_count);
4522         torture_assert_ntstatus_ok(torture, status,
4523                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4524         torture_assert_u64_equal(torture, far_count, 1,
4525                                  "unexpected response len");
4526         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4527                                  "unexpected allocation");
4528         torture_assert_u64_equal(torture, far_rsp[0].len,
4529                                  dealloc_chunk_len - 1,
4530                                  "unexpected far len");
4531
4532         /* sparse QAR with range one after hole */
4533         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4534                                     0,                          /* off */
4535                                     dealloc_chunk_len + 1,      /* len */
4536                                     &far_rsp, &far_count);
4537         torture_assert_ntstatus_ok(torture, status,
4538                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4539         torture_assert_u64_equal(torture, far_count, 1,
4540                                  "unexpected response len");
4541         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4542                                  "unexpected allocation");
4543         torture_assert_u64_equal(torture, far_rsp[0].len,
4544                                  dealloc_chunk_len,
4545                                  "unexpected far len");
4546
4547         /* sparse QAR with range one after hole from off=1 */
4548         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4549                                     1,                          /* off */
4550                                     dealloc_chunk_len,          /* len */
4551                                     &far_rsp, &far_count);
4552         torture_assert_ntstatus_ok(torture, status,
4553                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4554         torture_assert_u64_equal(torture, far_count, 1,
4555                                  "unexpected response len");
4556         torture_assert_u64_equal(torture, far_rsp[0].file_off, 1,
4557                                  "unexpected allocation");
4558         torture_assert_u64_equal(torture, far_rsp[0].len,
4559                                  dealloc_chunk_len - 1,
4560                                  "unexpected far len");
4561
4562         /* sparse QAR with range one before EOF from off=chunk_len-1 */
4563         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4564                                     dealloc_chunk_len - 1,      /* off */
4565                                     dealloc_chunk_len,          /* len */
4566                                     &far_rsp, &far_count);
4567         torture_assert_ntstatus_ok(torture, status,
4568                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4569         torture_assert_u64_equal(torture, far_count, 1,
4570                                  "unexpected response len");
4571         torture_assert_u64_equal(torture, far_rsp[0].file_off,
4572                                  dealloc_chunk_len - 1,
4573                                  "unexpected allocation");
4574         torture_assert_u64_equal(torture, far_rsp[0].len,
4575                                  1, "unexpected far len");
4576
4577         /* sparse QAR with range one after EOF from off=chunk_len+1 */
4578         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4579                                     dealloc_chunk_len + 1,      /* off */
4580                                     dealloc_chunk_len,          /* len */
4581                                     &far_rsp, &far_count);
4582         torture_assert_ntstatus_ok(torture, status,
4583                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4584         torture_assert_u64_equal(torture, far_count, 0,
4585                                  "unexpected response len");
4586         smb2_util_close(tree, fh);
4587         talloc_free(tmp_ctx);
4588         return true;
4589 }
4590
4591 /* test QAR with multi-range responses */
4592 static bool test_ioctl_sparse_qar_multi(struct torture_context *torture,
4593                                         struct smb2_tree *tree)
4594 {
4595         struct smb2_handle fh;
4596         NTSTATUS status;
4597         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4598         bool ok;
4599         uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
4600         uint64_t this_off;
4601         int i;
4602         struct file_alloced_range_buf *far_rsp = NULL;
4603         uint64_t far_count = 0;
4604
4605         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4606                                     FNAME, &fh, dealloc_chunk_len * 2,
4607                                     SEC_RIGHTS_FILE_ALL,
4608                                     FILE_ATTRIBUTE_NORMAL);
4609         torture_assert(torture, ok, "setup file");
4610
4611         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4612                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
4613         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4614         if (!ok) {
4615                 torture_skip(torture, "Sparse files not supported\n");
4616                 smb2_util_close(tree, fh);
4617         }
4618
4619         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4620         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4621
4622         /* each loop, write out two chunks and punch the first out */
4623         for (i = 0; i < 10; i++) {
4624                 this_off = i * dealloc_chunk_len * 2;
4625
4626                 ok = write_pattern(torture, tree, tmp_ctx, fh,
4627                                    this_off,                    /* off */
4628                                    dealloc_chunk_len * 2,       /* len */
4629                                    this_off);           /* pattern offset */
4630                 torture_assert(torture, ok, "write pattern");
4631
4632                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4633                                               this_off, /* off */
4634                                               this_off + dealloc_chunk_len);
4635                 torture_assert_ntstatus_ok(torture, status, "zero_data");
4636         }
4637
4638         /* should now have one separate region for each iteration */
4639         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4640                                     0,
4641                                     10 * dealloc_chunk_len * 2,
4642                                     &far_rsp, &far_count);
4643         torture_assert_ntstatus_ok(torture, status,
4644                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4645         if (far_count == 1) {
4646                 torture_comment(torture, "this FS doesn't deallocate 64K"
4647                                 "zeroed ranges in sparse files\n");
4648                 return true;    /* FS specific, not a failure */
4649         }
4650         torture_assert_u64_equal(torture, far_count, 10,
4651                                  "unexpected response len");
4652         for (i = 0; i < 10; i++) {
4653                 this_off = i * dealloc_chunk_len * 2;
4654
4655                 torture_assert_u64_equal(torture, far_rsp[i].file_off,
4656                                          this_off + dealloc_chunk_len,
4657                                          "unexpected allocation");
4658                 torture_assert_u64_equal(torture, far_rsp[i].len,
4659                                          dealloc_chunk_len,
4660                                          "unexpected far len");
4661         }
4662
4663         smb2_util_close(tree, fh);
4664         talloc_free(tmp_ctx);
4665         return true;
4666 }
4667
4668 static bool test_ioctl_sparse_qar_overflow(struct torture_context *torture,
4669                                            struct smb2_tree *tree)
4670 {
4671         struct smb2_handle fh;
4672         union smb_ioctl ioctl;
4673         struct file_alloced_range_buf far_buf;
4674         NTSTATUS status;
4675         enum ndr_err_code ndr_ret;
4676         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4677         bool ok;
4678
4679         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4680                                     FNAME, &fh, 1024, SEC_RIGHTS_FILE_ALL,
4681                                     FILE_ATTRIBUTE_NORMAL);
4682         torture_assert(torture, ok, "setup file");
4683
4684         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4685                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
4686         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4687         if (!ok) {
4688                 smb2_util_close(tree, fh);
4689                 torture_skip(torture, "Sparse files not supported\n");
4690         }
4691
4692         /* no allocated ranges, no space for range response, should pass */
4693         ZERO_STRUCT(ioctl);
4694         ioctl.smb2.level = RAW_IOCTL_SMB2;
4695         ioctl.smb2.in.file.handle = fh;
4696         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
4697         ioctl.smb2.in.max_response_size = 1024;
4698         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4699
4700         /* off + length wraps around to 511 */
4701         far_buf.file_off = 512;
4702         far_buf.len = 0xffffffffffffffffLL;
4703         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4704                                        &far_buf,
4705                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
4706         torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
4707
4708         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4709         torture_assert_ntstatus_equal(torture, status,
4710                                       NT_STATUS_INVALID_PARAMETER,
4711                                       "FSCTL_QUERY_ALLOCATED_RANGES overflow");
4712
4713         return true;
4714 }
4715
4716 static NTSTATUS test_ioctl_trim_supported(struct torture_context *torture,
4717                                           struct smb2_tree *tree,
4718                                           TALLOC_CTX *mem_ctx,
4719                                           struct smb2_handle *fh,
4720                                           bool *trim_support)
4721 {
4722         NTSTATUS status;
4723         union smb_fsinfo info;
4724
4725         ZERO_STRUCT(info);
4726         info.generic.level = RAW_QFS_SECTOR_SIZE_INFORMATION;
4727         info.generic.handle = *fh;
4728         status = smb2_getinfo_fs(tree, tree, &info);
4729         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
4730                 /*
4731                  * Windows < Server 2012, 8 etc. don't support this info level
4732                  * or the trim ioctl. Ignore the error and let the caller skip.
4733                  */
4734                 *trim_support = false;
4735                 return NT_STATUS_OK;
4736         } else if (!NT_STATUS_IS_OK(status)) {
4737                 return status;
4738         }
4739
4740         torture_comment(torture, "sector size info: lb/s=%u, pb/sA=%u, "
4741                         "pb/sP=%u, fse/sA=%u, flags=0x%x, bosa=%u, bopa=%u\n",
4742             (unsigned)info.sector_size_info.out.logical_bytes_per_sector,
4743             (unsigned)info.sector_size_info.out.phys_bytes_per_sector_atomic,
4744             (unsigned)info.sector_size_info.out.phys_bytes_per_sector_perf,
4745   (unsigned)info.sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic,
4746             (unsigned)info.sector_size_info.out.flags,
4747             (unsigned)info.sector_size_info.out.byte_off_sector_align,
4748             (unsigned)info.sector_size_info.out.byte_off_partition_align);
4749
4750         if (info.sector_size_info.out.flags & QFS_SSINFO_FLAGS_TRIM_ENABLED) {
4751                 *trim_support = true;
4752         } else {
4753                 *trim_support = false;
4754         }
4755         return NT_STATUS_OK;
4756 }
4757
4758 static bool test_setup_trim(struct torture_context *torture,
4759                             struct smb2_tree *tree,
4760                             TALLOC_CTX *mem_ctx,
4761                             uint32_t num_ranges,
4762                             struct smb2_handle *fh,
4763                             uint64_t file_size,
4764                             uint32_t desired_access,
4765                             struct fsctl_file_level_trim_req *trim_req,
4766                             union smb_ioctl *ioctl)
4767 {
4768         bool ok;
4769
4770         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
4771                                     fh, file_size, desired_access,
4772                                     FILE_ATTRIBUTE_NORMAL);
4773         torture_assert(torture, ok, "src file create fill");
4774
4775         ZERO_STRUCTPN(ioctl);
4776         ioctl->smb2.level = RAW_IOCTL_SMB2;
4777         ioctl->smb2.in.file.handle = *fh;
4778         ioctl->smb2.in.function = FSCTL_FILE_LEVEL_TRIM;
4779         ioctl->smb2.in.max_response_size
4780                                 = sizeof(struct fsctl_file_level_trim_rsp);
4781         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4782
4783         ZERO_STRUCTPN(trim_req);
4784         /* leave key as zero for now. TODO test locking with differing keys */
4785         trim_req->num_ranges = num_ranges;
4786         trim_req->ranges = talloc_zero_array(mem_ctx,
4787                                              struct file_level_trim_range,
4788                                              num_ranges);
4789         torture_assert(torture, (trim_req->ranges != NULL), "no memory for ranges");
4790
4791         return true;
4792 }
4793
4794 static bool test_ioctl_trim_simple(struct torture_context *torture,
4795                                    struct smb2_tree *tree)
4796 {
4797         struct smb2_handle fh;
4798         NTSTATUS status;
4799         union smb_ioctl ioctl;
4800         bool trim_supported;
4801         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4802         struct fsctl_file_level_trim_req trim_req;
4803         struct fsctl_file_level_trim_rsp trim_rsp;
4804         uint64_t trim_chunk_len = 64 * 1024;    /* trim 64K chunks */
4805         enum ndr_err_code ndr_ret;
4806         bool ok;
4807
4808         ok = test_setup_trim(torture, tree, tmp_ctx,
4809                              1, /* 1 range */
4810                              &fh, 2 * trim_chunk_len, /* fill 128K file */
4811                              SEC_RIGHTS_FILE_ALL,
4812                              &trim_req,
4813                              &ioctl);
4814         if (!ok) {
4815                 torture_fail(torture, "setup trim error");
4816         }
4817
4818         status = test_ioctl_trim_supported(torture, tree, tmp_ctx, &fh,
4819                                            &trim_supported);
4820         torture_assert_ntstatus_ok(torture, status, "fsinfo");
4821         if (!trim_supported) {
4822                 smb2_util_close(tree, fh);
4823                 talloc_free(tmp_ctx);
4824                 torture_skip(torture, "trim not supported\n");
4825         }
4826
4827         /* trim first chunk, leave second */
4828         trim_req.ranges[0].off = 0;
4829         trim_req.ranges[0].len = trim_chunk_len;
4830
4831         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx, &trim_req,
4832                        (ndr_push_flags_fn_t)ndr_push_fsctl_file_level_trim_req);
4833         torture_assert_ndr_success(torture, ndr_ret,
4834                                    "ndr_push_fsctl_file_level_trim_req");
4835
4836         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4837         torture_assert_ntstatus_ok(torture, status, "FILE_LEVEL_TRIM_RANGE");
4838
4839         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
4840                                        &trim_rsp,
4841                        (ndr_pull_flags_fn_t)ndr_pull_fsctl_file_level_trim_rsp);
4842         torture_assert_ndr_success(torture, ndr_ret,
4843                                    "ndr_pull_fsctl_file_level_trim_rsp");
4844
4845         torture_assert_int_equal(torture, trim_rsp.num_ranges_processed, 1, "");
4846
4847         /* second half of the file should remain consitent */
4848         ok = check_pattern(torture, tree, tmp_ctx, fh, trim_chunk_len,
4849                            trim_chunk_len, trim_chunk_len);
4850         torture_assert(torture, ok, "non-trimmed range inconsistent");
4851
4852         return true;
4853 }
4854
4855 static bool test_setup_dup_extents(struct torture_context *tctx,
4856                                    struct smb2_tree *tree,
4857                                    TALLOC_CTX *mem_ctx,
4858                                    struct smb2_handle *src_h,
4859                                    uint64_t src_size,
4860                                    uint32_t src_desired_access,
4861                                    struct smb2_handle *dest_h,
4862                                    uint64_t dest_size,
4863                                    uint32_t dest_desired_access,
4864                                    struct fsctl_dup_extents_to_file *dup_ext_buf,
4865                                    union smb_ioctl *ioctl)
4866 {
4867         bool ok;
4868
4869         ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME,
4870                                     src_h, src_size, src_desired_access,
4871                                     FILE_ATTRIBUTE_NORMAL);
4872         torture_assert(tctx, ok, "src file create fill");
4873
4874         ok = test_setup_create_fill(tctx, tree, mem_ctx, FNAME2,
4875                                     dest_h, dest_size, dest_desired_access,
4876                                     FILE_ATTRIBUTE_NORMAL);
4877         torture_assert(tctx, ok, "dest file create fill");
4878
4879         ZERO_STRUCTPN(ioctl);
4880         ioctl->smb2.level = RAW_IOCTL_SMB2;
4881         ioctl->smb2.in.file.handle = *dest_h;
4882         ioctl->smb2.in.function = FSCTL_DUP_EXTENTS_TO_FILE;
4883         ioctl->smb2.in.max_response_size = 0;
4884         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
4885
4886         ZERO_STRUCTPN(dup_ext_buf);
4887         smb2_push_handle(dup_ext_buf->source_fid, src_h);
4888
4889         return true;
4890 }
4891
4892 static bool test_ioctl_dup_extents_simple(struct torture_context *tctx,
4893                                           struct smb2_tree *tree)
4894 {
4895         struct smb2_handle src_h;
4896         struct smb2_handle dest_h;
4897         NTSTATUS status;
4898         union smb_ioctl ioctl;
4899         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4900         struct fsctl_dup_extents_to_file dup_ext_buf;
4901         enum ndr_err_code ndr_ret;
4902         union smb_fileinfo io;
4903         union smb_setfileinfo sinfo;
4904         bool ok;
4905
4906         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
4907                                     &src_h, 4096, /* fill 4096 byte src file */
4908                                     SEC_RIGHTS_FILE_ALL,
4909                                     &dest_h, 0, /* 0 byte dest file */
4910                                     SEC_RIGHTS_FILE_ALL,
4911                                     &dup_ext_buf,
4912                                     &ioctl);
4913         if (!ok) {
4914                 torture_fail(tctx, "setup dup extents error");
4915         }
4916
4917         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
4918                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
4919         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
4920         if (!ok) {
4921                 smb2_util_close(tree, src_h);
4922                 smb2_util_close(tree, dest_h);
4923                 talloc_free(tmp_ctx);
4924                 torture_skip(tctx, "block refcounting not supported\n");
4925         }
4926
4927         /* extend dest to match src len */
4928         ZERO_STRUCT(sinfo);
4929         sinfo.end_of_file_info.level =
4930                 RAW_SFILEINFO_END_OF_FILE_INFORMATION;
4931         sinfo.end_of_file_info.in.file.handle = dest_h;
4932         sinfo.end_of_file_info.in.size = 4096;
4933         status = smb2_setinfo_file(tree, &sinfo);
4934         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
4935
4936         /* copy all src file data */
4937         dup_ext_buf.source_off = 0;
4938         dup_ext_buf.target_off = 0;
4939         dup_ext_buf.byte_count = 4096;
4940
4941         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
4942                                        &dup_ext_buf,
4943                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
4944         torture_assert_ndr_success(tctx, ndr_ret,
4945                                    "ndr_push_fsctl_dup_extents_to_file");
4946
4947         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
4948         torture_assert_ntstatus_ok(tctx, status,
4949                                    "FSCTL_DUP_EXTENTS_TO_FILE");
4950
4951         /* the file size shouldn't have been changed by this operation! */
4952         ZERO_STRUCT(io);
4953         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
4954         io.generic.in.file.handle = dest_h;
4955         status = smb2_getinfo_file(tree, tmp_ctx, &io);
4956         torture_assert_ntstatus_ok(tctx, status, "getinfo");
4957         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
4958                                  4096, "size after IO");
4959
4960         smb2_util_close(tree, src_h);
4961         smb2_util_close(tree, dest_h);
4962
4963         /* reopen for pattern check */
4964         ok = test_setup_open(tctx, tree, tmp_ctx, FNAME, &src_h,
4965                              SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
4966         torture_assert_ntstatus_ok(tctx, status, "src open after dup");
4967         ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
4968                              SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
4969         torture_assert_ntstatus_ok(tctx, status, "dest open after dup");
4970
4971         ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
4972         if (!ok) {
4973                 torture_fail(tctx, "inconsistent src file data");
4974         }
4975
4976         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
4977         if (!ok) {
4978                 torture_fail(tctx, "inconsistent dest file data");
4979         }
4980
4981         smb2_util_close(tree, src_h);
4982         smb2_util_close(tree, dest_h);
4983         talloc_free(tmp_ctx);
4984         return true;
4985 }
4986
4987 static bool test_ioctl_dup_extents_len_beyond_dest(struct torture_context *tctx,
4988                                                    struct smb2_tree *tree)
4989 {
4990         struct smb2_handle src_h;
4991         struct smb2_handle dest_h;
4992         NTSTATUS status;
4993         union smb_ioctl ioctl;
4994         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4995         struct fsctl_dup_extents_to_file dup_ext_buf;
4996         enum ndr_err_code ndr_ret;
4997         union smb_fileinfo io;
4998         union smb_setfileinfo sinfo;
4999         bool ok;
5000
5001         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5002                                     &src_h, 32768, /* fill 32768 byte src file */
5003                                     SEC_RIGHTS_FILE_ALL,
5004                                     &dest_h, 0, /* 0 byte dest file */
5005                                     SEC_RIGHTS_FILE_ALL,
5006                                     &dup_ext_buf,
5007                                     &ioctl);
5008         if (!ok) {
5009                 torture_fail(tctx, "setup dup extents error");
5010         }
5011
5012         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5013                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5014         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5015         if (!ok) {
5016                 smb2_util_close(tree, src_h);
5017                 smb2_util_close(tree, dest_h);
5018                 talloc_free(tmp_ctx);
5019                 torture_skip(tctx, "block refcounting not supported\n");
5020         }
5021
5022         ZERO_STRUCT(io);
5023         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5024         io.generic.in.file.handle = dest_h;
5025         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5026         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5027         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5028                                  0, "size after IO");
5029
5030         /* copy all src file data */
5031         dup_ext_buf.source_off = 0;
5032         dup_ext_buf.target_off = 0;
5033         dup_ext_buf.byte_count = 32768;
5034
5035         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5036                                        &dup_ext_buf,
5037                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5038         torture_assert_ndr_success(tctx, ndr_ret,
5039                                    "ndr_push_fsctl_dup_extents_to_file");
5040
5041         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5042 #if 0
5043         /*
5044          * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply - this should fail, but
5045          * passes against WS2016 RTM!
5046          */
5047         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5048                                    "FSCTL_DUP_EXTENTS_TO_FILE");
5049 #endif
5050
5051         /* the file sizes shouldn't have been changed */
5052         ZERO_STRUCT(io);
5053         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5054         io.generic.in.file.handle = src_h;
5055         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5056         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5057         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5058                                  32768, "size after IO");
5059
5060         ZERO_STRUCT(io);
5061         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5062         io.generic.in.file.handle = dest_h;
5063         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5064         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5065         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5066                                  0, "size after IO");
5067
5068         /* extend dest */
5069         ZERO_STRUCT(sinfo);
5070         sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5071         sinfo.end_of_file_info.in.file.handle = dest_h;
5072         sinfo.end_of_file_info.in.size = 32768;
5073         status = smb2_setinfo_file(tree, &sinfo);
5074         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5075
5076         ok = check_zero(tctx, tree, tmp_ctx, dest_h, 0, 32768);
5077         if (!ok) {
5078                 torture_fail(tctx, "inconsistent file data");
5079         }
5080
5081         /* reissue ioctl, now with enough space */
5082         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5083         torture_assert_ntstatus_ok(tctx, status,
5084                                    "FSCTL_DUP_EXTENTS_TO_FILE");
5085
5086         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5087         if (!ok) {
5088                 torture_fail(tctx, "inconsistent file data");
5089         }
5090
5091         smb2_util_close(tree, src_h);
5092         smb2_util_close(tree, dest_h);
5093         talloc_free(tmp_ctx);
5094         return true;
5095 }
5096
5097 static bool test_ioctl_dup_extents_len_beyond_src(struct torture_context *tctx,
5098                                                   struct smb2_tree *tree)
5099 {
5100         struct smb2_handle src_h;
5101         struct smb2_handle dest_h;
5102         NTSTATUS status;
5103         union smb_ioctl ioctl;
5104         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5105         struct fsctl_dup_extents_to_file dup_ext_buf;
5106         enum ndr_err_code ndr_ret;
5107         union smb_fileinfo io;
5108         bool ok;
5109
5110         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5111                                     &src_h, 32768, /* fill 32768 byte src file */
5112                                     SEC_RIGHTS_FILE_ALL,
5113                                     &dest_h, 0, /* 0 byte dest file */
5114                                     SEC_RIGHTS_FILE_ALL,
5115                                     &dup_ext_buf,
5116                                     &ioctl);
5117         if (!ok) {
5118                 torture_fail(tctx, "setup dup extents error");
5119         }
5120
5121         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5122                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5123         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5124         if (!ok) {
5125                 smb2_util_close(tree, src_h);
5126                 smb2_util_close(tree, dest_h);
5127                 talloc_free(tmp_ctx);
5128                 torture_skip(tctx, "block refcounting not supported\n");
5129         }
5130
5131         ZERO_STRUCT(io);
5132         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5133         io.generic.in.file.handle = dest_h;
5134         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5135         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5136         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5137                                  0, "size after IO");
5138
5139         /* exceed src file len */
5140         dup_ext_buf.source_off = 0;
5141         dup_ext_buf.target_off = 0;
5142         dup_ext_buf.byte_count = 32768 * 2;
5143
5144         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5145                                        &dup_ext_buf,
5146                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5147         torture_assert_ndr_success(tctx, ndr_ret,
5148                                    "ndr_push_fsctl_dup_extents_to_file");
5149
5150         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5151         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5152                                    "FSCTL_DUP_EXTENTS_TO_FILE");
5153
5154         /* the file sizes shouldn't have been changed */
5155         ZERO_STRUCT(io);
5156         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5157         io.generic.in.file.handle = src_h;
5158         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5159         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5160         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5161                                  32768, "size after IO");
5162
5163         ZERO_STRUCT(io);
5164         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5165         io.generic.in.file.handle = dest_h;
5166         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5167         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5168         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5169                                  0, "size after IO");
5170
5171         smb2_util_close(tree, src_h);
5172         smb2_util_close(tree, dest_h);
5173         talloc_free(tmp_ctx);
5174         return true;
5175 }
5176
5177 static bool test_ioctl_dup_extents_len_zero(struct torture_context *tctx,
5178                                             struct smb2_tree *tree)
5179 {
5180         struct smb2_handle src_h;
5181         struct smb2_handle dest_h;
5182         NTSTATUS status;
5183         union smb_ioctl ioctl;
5184         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5185         struct fsctl_dup_extents_to_file dup_ext_buf;
5186         enum ndr_err_code ndr_ret;
5187         union smb_fileinfo io;
5188         bool ok;
5189
5190         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5191                                     &src_h, 32768, /* fill 32768 byte src file */
5192                                     SEC_RIGHTS_FILE_ALL,
5193                                     &dest_h, 0, /* 0 byte dest file */
5194                                     SEC_RIGHTS_FILE_ALL,
5195                                     &dup_ext_buf,
5196                                     &ioctl);
5197         if (!ok) {
5198                 torture_fail(tctx, "setup dup extents error");
5199         }
5200
5201         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5202                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5203         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5204         if (!ok) {
5205                 smb2_util_close(tree, src_h);
5206                 smb2_util_close(tree, dest_h);
5207                 talloc_free(tmp_ctx);
5208                 torture_skip(tctx, "block refcounting not supported\n");
5209         }
5210
5211         ZERO_STRUCT(io);
5212         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5213         io.generic.in.file.handle = dest_h;
5214         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5215         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5216         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5217                                  0, "size after IO");
5218
5219         dup_ext_buf.source_off = 0;
5220         dup_ext_buf.target_off = 0;
5221         dup_ext_buf.byte_count = 0;
5222
5223         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5224                                        &dup_ext_buf,
5225                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5226         torture_assert_ndr_success(tctx, ndr_ret,
5227                                    "ndr_push_fsctl_dup_extents_to_file");
5228
5229         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5230         torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5231
5232         /* the file sizes shouldn't have been changed */
5233         ZERO_STRUCT(io);
5234         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5235         io.generic.in.file.handle = src_h;
5236         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5237         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5238         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5239                                  32768, "size after IO");
5240
5241         ZERO_STRUCT(io);
5242         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5243         io.generic.in.file.handle = dest_h;
5244         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5245         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5246         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5247                                  0, "size after IO");
5248
5249         smb2_util_close(tree, src_h);
5250         smb2_util_close(tree, dest_h);
5251         talloc_free(tmp_ctx);
5252         return true;
5253 }
5254
5255 static bool test_ioctl_dup_extents_sparse_src(struct torture_context *tctx,
5256                                               struct smb2_tree *tree)
5257 {
5258         struct smb2_handle src_h;
5259         struct smb2_handle dest_h;
5260         NTSTATUS status;
5261         union smb_ioctl ioctl;
5262         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5263         struct fsctl_dup_extents_to_file dup_ext_buf;
5264         enum ndr_err_code ndr_ret;
5265         union smb_setfileinfo sinfo;
5266         bool ok;
5267
5268         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5269                                     &src_h, 0, /* filled after sparse flag */
5270                                     SEC_RIGHTS_FILE_ALL,
5271                                     &dest_h, 0, /* 0 byte dest file */
5272                                     SEC_RIGHTS_FILE_ALL,
5273                                     &dup_ext_buf,
5274                                     &ioctl);
5275         if (!ok) {
5276                 torture_fail(tctx, "setup dup extents error");
5277         }
5278
5279         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5280                                          FILE_SUPPORTS_BLOCK_REFCOUNTING
5281                                          | FILE_SUPPORTS_SPARSE_FILES, &ok);
5282         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5283         if (!ok) {
5284                 smb2_util_close(tree, src_h);
5285                 smb2_util_close(tree, dest_h);
5286                 talloc_free(tmp_ctx);
5287                 torture_skip(tctx,
5288                         "block refcounting and sparse files not supported\n");
5289         }
5290
5291         /* set sparse flag on src */
5292         status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5293         torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5294
5295         ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5296         torture_assert(tctx, ok, "write pattern");
5297
5298         /* extend dest */
5299         ZERO_STRUCT(sinfo);
5300         sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5301         sinfo.end_of_file_info.in.file.handle = dest_h;
5302         sinfo.end_of_file_info.in.size = 4096;
5303         status = smb2_setinfo_file(tree, &sinfo);
5304         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5305
5306         /* copy all src file data */
5307         dup_ext_buf.source_off = 0;
5308         dup_ext_buf.target_off = 0;
5309         dup_ext_buf.byte_count = 4096;
5310
5311         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5312                                        &dup_ext_buf,
5313                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5314         torture_assert_ndr_success(tctx, ndr_ret,
5315                                    "ndr_push_fsctl_dup_extents_to_file");
5316
5317         /*
5318          * src is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5319          * Reply...  STATUS_NOT_SUPPORTED: Target file is sparse, while source
5320          *                                 is a non-sparse file.
5321          */
5322         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5323         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5324                                       "FSCTL_DUP_EXTENTS_TO_FILE");
5325
5326         smb2_util_close(tree, src_h);
5327         smb2_util_close(tree, dest_h);
5328         talloc_free(tmp_ctx);
5329         return true;
5330 }
5331
5332 static bool test_ioctl_dup_extents_sparse_dest(struct torture_context *tctx,
5333                                                struct smb2_tree *tree)
5334 {
5335         struct smb2_handle src_h;
5336         struct smb2_handle dest_h;
5337         NTSTATUS status;
5338         union smb_ioctl ioctl;
5339         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5340         struct fsctl_dup_extents_to_file dup_ext_buf;
5341         enum ndr_err_code ndr_ret;
5342         union smb_setfileinfo sinfo;
5343         bool ok;
5344
5345         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5346                                     &src_h, 4096, /* fill 4096 byte src file */
5347                                     SEC_RIGHTS_FILE_ALL,
5348                                     &dest_h, 0, /* 0 byte dest file */
5349                                     SEC_RIGHTS_FILE_ALL,
5350                                     &dup_ext_buf,
5351                                     &ioctl);
5352         if (!ok) {
5353                 torture_fail(tctx, "setup dup extents error");
5354         }
5355
5356         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5357                                          FILE_SUPPORTS_BLOCK_REFCOUNTING
5358                                          | FILE_SUPPORTS_SPARSE_FILES, &ok);
5359         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5360         if (!ok) {
5361                 smb2_util_close(tree, src_h);
5362                 smb2_util_close(tree, dest_h);
5363                 talloc_free(tmp_ctx);
5364                 torture_skip(tctx,
5365                         "block refcounting and sparse files not supported\n");
5366         }
5367
5368         /* set sparse flag on dest */
5369         status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5370         torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5371
5372         /* extend dest */
5373         ZERO_STRUCT(sinfo);
5374         sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5375         sinfo.end_of_file_info.in.file.handle = dest_h;
5376         sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5377         status = smb2_setinfo_file(tree, &sinfo);
5378         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5379
5380         /* copy all src file data */
5381         dup_ext_buf.source_off = 0;
5382         dup_ext_buf.target_off = 0;
5383         dup_ext_buf.byte_count = 4096;
5384
5385         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5386                                        &dup_ext_buf,
5387                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5388         torture_assert_ndr_success(tctx, ndr_ret,
5389                                    "ndr_push_fsctl_dup_extents_to_file");
5390
5391         /*
5392          * dest is sparse, but spec says: 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE
5393          * Reply...  STATUS_NOT_SUPPORTED: Target file is sparse, while source
5394          *                                 is a non-sparse file.
5395          */
5396         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5397         torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5398
5399         smb2_util_close(tree, src_h);
5400         smb2_util_close(tree, dest_h);
5401         talloc_free(tmp_ctx);
5402         return true;
5403 }
5404
5405 static bool test_ioctl_dup_extents_sparse_both(struct torture_context *tctx,
5406                                                struct smb2_tree *tree)
5407 {
5408         struct smb2_handle src_h;
5409         struct smb2_handle dest_h;
5410         NTSTATUS status;
5411         union smb_ioctl ioctl;
5412         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5413         struct fsctl_dup_extents_to_file dup_ext_buf;
5414         enum ndr_err_code ndr_ret;
5415         union smb_setfileinfo sinfo;
5416         bool ok;
5417
5418         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5419                                     &src_h, 0, /* fill 4096 byte src file */
5420                                     SEC_RIGHTS_FILE_ALL,
5421                                     &dest_h, 0, /* 0 byte dest file */
5422                                     SEC_RIGHTS_FILE_ALL,
5423                                     &dup_ext_buf,
5424                                     &ioctl);
5425         if (!ok) {
5426                 torture_fail(tctx, "setup dup extents error");
5427         }
5428
5429         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5430                                          FILE_SUPPORTS_BLOCK_REFCOUNTING
5431                                          | FILE_SUPPORTS_SPARSE_FILES, &ok);
5432         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5433         if (!ok) {
5434                 smb2_util_close(tree, src_h);
5435                 smb2_util_close(tree, dest_h);
5436                 talloc_free(tmp_ctx);
5437                 torture_skip(tctx,
5438                         "block refcounting and sparse files not supported\n");
5439         }
5440
5441         /* set sparse flag on src and dest */
5442         status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, src_h, true);
5443         torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5444         status = test_ioctl_sparse_req(tctx, tmp_ctx, tree, dest_h, true);
5445         torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_SPARSE");
5446
5447         ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5448         torture_assert(tctx, ok, "write pattern");
5449
5450         /* extend dest */
5451         ZERO_STRUCT(sinfo);
5452         sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5453         sinfo.end_of_file_info.in.file.handle = dest_h;
5454         sinfo.end_of_file_info.in.size = 4096;
5455         status = smb2_setinfo_file(tree, &sinfo);
5456         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5457
5458         /* copy all src file data */
5459         dup_ext_buf.source_off = 0;
5460         dup_ext_buf.target_off = 0;
5461         dup_ext_buf.byte_count = 4096;
5462
5463         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5464                                        &dup_ext_buf,
5465                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5466         torture_assert_ndr_success(tctx, ndr_ret,
5467                                    "ndr_push_fsctl_dup_extents_to_file");
5468
5469         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5470         torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5471
5472         smb2_util_close(tree, src_h);
5473         smb2_util_close(tree, dest_h);
5474
5475         /* reopen for pattern check */
5476         ok = test_setup_open(tctx, tree, tmp_ctx, FNAME2, &dest_h,
5477                              SEC_RIGHTS_FILE_ALL, FILE_ATTRIBUTE_NORMAL);
5478         torture_assert_ntstatus_ok(tctx, status, "dest open ater dup");
5479
5480         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5481         if (!ok) {
5482                 torture_fail(tctx, "inconsistent file data");
5483         }
5484
5485         smb2_util_close(tree, dest_h);
5486         talloc_free(tmp_ctx);
5487         return true;
5488 }
5489
5490 static bool test_ioctl_dup_extents_src_is_dest(struct torture_context *tctx,
5491                                            struct smb2_tree *tree)
5492 {
5493         struct smb2_handle src_h;
5494         struct smb2_handle dest_h;
5495         NTSTATUS status;
5496         union smb_ioctl ioctl;
5497         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5498         struct fsctl_dup_extents_to_file dup_ext_buf;
5499         enum ndr_err_code ndr_ret;
5500         union smb_fileinfo io;
5501         bool ok;
5502
5503         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5504                                     &src_h, 32768, /* fill 32768 byte src file */
5505                                     SEC_RIGHTS_FILE_ALL,
5506                                     &dest_h, 0,
5507                                     SEC_RIGHTS_FILE_ALL,
5508                                     &dup_ext_buf,
5509                                     &ioctl);
5510         if (!ok) {
5511                 torture_fail(tctx, "setup dup extents error");
5512         }
5513         /* dest_h not needed for this test */
5514         smb2_util_close(tree, dest_h);
5515
5516         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5517                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5518         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5519         if (!ok) {
5520                 smb2_util_close(tree, src_h);
5521                 talloc_free(tmp_ctx);
5522                 torture_skip(tctx, "block refcounting not supported\n");
5523         }
5524
5525         /* src and dest are the same file handle */
5526         ioctl.smb2.in.file.handle = src_h;
5527
5528         /* no overlap between src and tgt */
5529         dup_ext_buf.source_off = 0;
5530         dup_ext_buf.target_off = 16384;
5531         dup_ext_buf.byte_count = 16384;
5532
5533         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5534                                        &dup_ext_buf,
5535                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5536         torture_assert_ndr_success(tctx, ndr_ret,
5537                                    "ndr_push_fsctl_dup_extents_to_file");
5538
5539         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5540         torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5541
5542         /* the file size shouldn't have been changed */
5543         ZERO_STRUCT(io);
5544         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
5545         io.generic.in.file.handle = src_h;
5546         status = smb2_getinfo_file(tree, tmp_ctx, &io);
5547         torture_assert_ntstatus_ok(tctx, status, "getinfo");
5548         torture_assert_int_equal(tctx, (int)io.all_info2.out.size,
5549                                  32768, "size after IO");
5550
5551         ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 16384, 0);
5552         if (!ok) {
5553                 torture_fail(tctx, "inconsistent file data");
5554         }
5555         ok = check_pattern(tctx, tree, tmp_ctx, src_h, 16384, 16384, 0);
5556         if (!ok) {
5557                 torture_fail(tctx, "inconsistent file data");
5558         }
5559
5560         smb2_util_close(tree, src_h);
5561         talloc_free(tmp_ctx);
5562         return true;
5563 }
5564
5565 /*
5566  * unlike copy-chunk, dup extents doesn't support overlapping ranges between
5567  * source and target. This makes it a *lot* cleaner to implement on the server.
5568  */
5569 static bool
5570 test_ioctl_dup_extents_src_is_dest_overlap(struct torture_context *tctx,
5571                                            struct smb2_tree *tree)
5572 {
5573         struct smb2_handle src_h;
5574         struct smb2_handle dest_h;
5575         NTSTATUS status;
5576         union smb_ioctl ioctl;
5577         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5578         struct fsctl_dup_extents_to_file dup_ext_buf;
5579         enum ndr_err_code ndr_ret;
5580         union smb_fileinfo io;
5581         bool ok;
5582
5583         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5584                                     &src_h, 32768, /* fill 32768 byte src file */
5585                                     SEC_RIGHTS_FILE_ALL,
5586                                     &dest_h, 0,
5587                                     SEC_RIGHTS_FILE_ALL,
5588                                     &dup_ext_buf,
5589                                     &ioctl);
5590         if (!ok) {
5591                 torture_fail(tctx, "setup dup extents error");
5592         }
5593         /* dest_h not needed for this test */
5594         smb2_util_close(tree, dest_h);
5595
5596         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5597                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5598         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5599         if (!ok) {
5600                 smb2_util_close(tree, src_h);
5601                 talloc_free(tmp_ctx);
5602                 torture_skip(tctx, "block refcounting not supported\n");
5603         }
5604
5605         /* src and dest are the same file handle */
5606         ioctl.smb2.in.file.handle = src_h;
5607
5608         /* 8K overlap between src and tgt */
5609         dup_ext_buf.source_off = 0;
5610         dup_ext_buf.target_off = 8192;
5611         dup_ext_buf.byte_count = 16384;
5612
5613         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5614                                        &dup_ext_buf,
5615                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5616         torture_assert_ndr_success(tctx, ndr_ret,
5617                                    "ndr_push_fsctl_dup_extents_to_file");
5618
5619         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5620         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5621                                       "FSCTL_DUP_EXTENTS_TO_FILE");
5622
5623         /* the file size and data should match beforehand */
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, 32768, 0);
5633         if (!ok) {
5634                 torture_fail(tctx, "inconsistent file data");
5635         }
5636
5637         smb2_util_close(tree, src_h);
5638         talloc_free(tmp_ctx);
5639         return true;
5640 }
5641
5642 /*
5643  * The compression tests won't run against Windows servers yet - ReFS doesn't
5644  * (yet) offer support for compression.
5645  */
5646 static bool test_ioctl_dup_extents_compressed_src(struct torture_context *tctx,
5647                                                   struct smb2_tree *tree)
5648 {
5649         struct smb2_handle src_h;
5650         struct smb2_handle dest_h;
5651         NTSTATUS status;
5652         union smb_ioctl ioctl;
5653         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5654         struct fsctl_dup_extents_to_file dup_ext_buf;
5655         enum ndr_err_code ndr_ret;
5656         union smb_setfileinfo sinfo;
5657         bool ok;
5658
5659         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5660                                     &src_h, 0, /* filled after compressed flag */
5661                                     SEC_RIGHTS_FILE_ALL,
5662                                     &dest_h, 0,
5663                                     SEC_RIGHTS_FILE_ALL,
5664                                     &dup_ext_buf,
5665                                     &ioctl);
5666         if (!ok) {
5667                 torture_fail(tctx, "setup dup extents error");
5668         }
5669
5670         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5671                                          FILE_SUPPORTS_BLOCK_REFCOUNTING
5672                                          | FILE_FILE_COMPRESSION, &ok);
5673         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5674         if (!ok) {
5675                 smb2_util_close(tree, src_h);
5676                 smb2_util_close(tree, dest_h);
5677                 talloc_free(tmp_ctx);
5678                 torture_skip(tctx,
5679                         "block refcounting and compressed files not supported\n");
5680         }
5681
5682         /* set compressed flag on src */
5683         status = test_ioctl_compress_set(tctx, tmp_ctx, tree, src_h,
5684                                          COMPRESSION_FORMAT_DEFAULT);
5685         torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5686
5687         ok = write_pattern(tctx, tree, tmp_ctx, src_h, 0, 4096, 0);
5688         torture_assert(tctx, ok, "write pattern");
5689
5690         /* extend dest */
5691         ZERO_STRUCT(sinfo);
5692         sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5693         sinfo.end_of_file_info.in.file.handle = dest_h;
5694         sinfo.end_of_file_info.in.size = 4096;
5695         status = smb2_setinfo_file(tree, &sinfo);
5696         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5697
5698         /* copy all src file data */
5699         dup_ext_buf.source_off = 0;
5700         dup_ext_buf.target_off = 0;
5701         dup_ext_buf.byte_count = 4096;
5702
5703         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5704                                        &dup_ext_buf,
5705                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5706         torture_assert_ndr_success(tctx, ndr_ret,
5707                                    "ndr_push_fsctl_dup_extents_to_file");
5708
5709         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5710         torture_assert_ntstatus_ok(tctx, status,
5711                                    "FSCTL_DUP_EXTENTS_TO_FILE");
5712
5713         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 4096, 0);
5714         if (!ok) {
5715                 torture_fail(tctx, "inconsistent file data");
5716         }
5717
5718         smb2_util_close(tree, src_h);
5719         smb2_util_close(tree, dest_h);
5720         talloc_free(tmp_ctx);
5721         return true;
5722 }
5723
5724 static bool test_ioctl_dup_extents_compressed_dest(struct torture_context *tctx,
5725                                                struct smb2_tree *tree)
5726 {
5727         struct smb2_handle src_h;
5728         struct smb2_handle dest_h;
5729         NTSTATUS status;
5730         union smb_ioctl ioctl;
5731         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5732         struct fsctl_dup_extents_to_file dup_ext_buf;
5733         enum ndr_err_code ndr_ret;
5734         union smb_setfileinfo sinfo;
5735         bool ok;
5736
5737         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5738                                     &src_h, 4096,
5739                                     SEC_RIGHTS_FILE_ALL,
5740                                     &dest_h, 0,
5741                                     SEC_RIGHTS_FILE_ALL,
5742                                     &dup_ext_buf,
5743                                     &ioctl);
5744         if (!ok) {
5745                 torture_fail(tctx, "setup dup extents error");
5746         }
5747
5748         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5749                                          FILE_SUPPORTS_BLOCK_REFCOUNTING
5750                                          | FILE_FILE_COMPRESSION, &ok);
5751         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5752         if (!ok) {
5753                 smb2_util_close(tree, src_h);
5754                 smb2_util_close(tree, dest_h);
5755                 talloc_free(tmp_ctx);
5756                 torture_skip(tctx,
5757                         "block refcounting and compressed files not supported\n");
5758         }
5759
5760         /* set compressed flag on dest */
5761         status = test_ioctl_compress_set(tctx, tmp_ctx, tree, dest_h,
5762                                          COMPRESSION_FORMAT_DEFAULT);
5763         torture_assert_ntstatus_ok(tctx, status, "FSCTL_SET_COMPRESSION");
5764
5765         /* extend dest */
5766         ZERO_STRUCT(sinfo);
5767         sinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
5768         sinfo.end_of_file_info.in.file.handle = dest_h;
5769         sinfo.end_of_file_info.in.size = dup_ext_buf.byte_count;
5770         status = smb2_setinfo_file(tree, &sinfo);
5771         torture_assert_ntstatus_ok(tctx, status, "smb2_setinfo_file");
5772
5773         /* copy all src file data */
5774         dup_ext_buf.source_off = 0;
5775         dup_ext_buf.target_off = 0;
5776         dup_ext_buf.byte_count = 4096;
5777
5778         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5779                                        &dup_ext_buf,
5780                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5781         torture_assert_ndr_success(tctx, ndr_ret,
5782                                    "ndr_push_fsctl_dup_extents_to_file");
5783
5784         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5785         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_SUPPORTED,
5786                                       "FSCTL_DUP_EXTENTS_TO_FILE");
5787
5788         smb2_util_close(tree, src_h);
5789         smb2_util_close(tree, dest_h);
5790         talloc_free(tmp_ctx);
5791         return true;
5792 }
5793
5794 static bool test_ioctl_dup_extents_bad_handle(struct torture_context *tctx,
5795                                               struct smb2_tree *tree)
5796 {
5797         struct smb2_handle src_h;
5798         struct smb2_handle dest_h;
5799         struct smb2_handle bogus_h;
5800         NTSTATUS status;
5801         union smb_ioctl ioctl;
5802         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5803         struct fsctl_dup_extents_to_file dup_ext_buf;
5804         enum ndr_err_code ndr_ret;
5805         bool ok;
5806
5807         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5808                                     &src_h, 32768, /* fill 32768 byte src file */
5809                                     SEC_RIGHTS_FILE_ALL,
5810                                     &dest_h, 32768,
5811                                     SEC_RIGHTS_FILE_ALL,
5812                                     &dup_ext_buf,
5813                                     &ioctl);
5814         if (!ok) {
5815                 torture_fail(tctx, "setup dup extents error");
5816         }
5817
5818         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5819                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5820         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5821         if (!ok) {
5822                 smb2_util_close(tree, src_h);
5823                 smb2_util_close(tree, dest_h);
5824                 talloc_free(tmp_ctx);
5825                 torture_skip(tctx, "block refcounting not supported\n");
5826         }
5827
5828         /* open and close a file, keeping the handle as now a "bogus" handle */
5829         ok = test_setup_create_fill(tctx, tree, tmp_ctx, "bogus_file",
5830                                     &bogus_h, 0, SEC_RIGHTS_FILE_ALL,
5831                                     FILE_ATTRIBUTE_NORMAL);
5832         torture_assert(tctx, ok, "bogus file create fill");
5833         smb2_util_close(tree, bogus_h);
5834
5835         /* bogus dest file handle */
5836         ioctl.smb2.in.file.handle = bogus_h;
5837
5838         dup_ext_buf.source_off = 0;
5839         dup_ext_buf.target_off = 0;
5840         dup_ext_buf.byte_count = 32768;
5841
5842         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5843                                        &dup_ext_buf,
5844                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5845         torture_assert_ndr_success(tctx, ndr_ret,
5846                                    "ndr_push_fsctl_dup_extents_to_file");
5847
5848         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5849         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_FILE_CLOSED,
5850                                       "FSCTL_DUP_EXTENTS_TO_FILE");
5851
5852         ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5853         if (!ok) {
5854                 torture_fail(tctx, "inconsistent file data");
5855         }
5856         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5857         if (!ok) {
5858                 torture_fail(tctx, "inconsistent file data");
5859         }
5860
5861         /* reinstate dest, add bogus src file handle */
5862         ioctl.smb2.in.file.handle = dest_h;
5863         smb2_push_handle(dup_ext_buf.source_fid, &bogus_h);
5864
5865         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5866                                        &dup_ext_buf,
5867                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5868         torture_assert_ndr_success(tctx, ndr_ret,
5869                                    "ndr_push_fsctl_dup_extents_to_file");
5870
5871         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5872         torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_HANDLE,
5873                                       "FSCTL_DUP_EXTENTS_TO_FILE");
5874
5875         ok = check_pattern(tctx, tree, tmp_ctx, src_h, 0, 32768, 0);
5876         if (!ok) {
5877                 torture_fail(tctx, "inconsistent file data");
5878         }
5879         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5880         if (!ok) {
5881                 torture_fail(tctx, "inconsistent file data");
5882         }
5883
5884         smb2_util_close(tree, src_h);
5885         smb2_util_close(tree, dest_h);
5886         talloc_free(tmp_ctx);
5887         return true;
5888 }
5889
5890 static bool test_ioctl_dup_extents_src_lck(struct torture_context *tctx,
5891                                            struct smb2_tree *tree)
5892 {
5893         struct smb2_handle src_h;
5894         struct smb2_handle src_h2;
5895         struct smb2_handle dest_h;
5896         NTSTATUS status;
5897         union smb_ioctl ioctl;
5898         TALLOC_CTX *tmp_ctx = talloc_new(tree);
5899         struct fsctl_dup_extents_to_file dup_ext_buf;
5900         enum ndr_err_code ndr_ret;
5901         bool ok;
5902         struct smb2_lock lck;
5903         struct smb2_lock_element el[1];
5904
5905         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
5906                                     &src_h, 32768, /* fill 32768 byte src file */
5907                                     SEC_RIGHTS_FILE_ALL,
5908                                     &dest_h, 0,
5909                                     SEC_RIGHTS_FILE_ALL,
5910                                     &dup_ext_buf,
5911                                     &ioctl);
5912         if (!ok) {
5913                 torture_fail(tctx, "setup dup extents error");
5914         }
5915
5916         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
5917                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
5918         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
5919         if (!ok) {
5920                 smb2_util_close(tree, src_h);
5921                 smb2_util_close(tree, dest_h);
5922                 talloc_free(tmp_ctx);
5923                 torture_skip(tctx, "block refcounting not supported\n");
5924         }
5925
5926         /* dest pattern is different to src */
5927         ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
5928         torture_assert(tctx, ok, "write pattern");
5929
5930         /* setup dup ext req, values used for locking */
5931         dup_ext_buf.source_off = 0;
5932         dup_ext_buf.target_off = 0;
5933         dup_ext_buf.byte_count = 32768;
5934
5935         /* open and lock the dup extents src file */
5936         status = torture_smb2_testfile(tree, FNAME, &src_h2);
5937         torture_assert_ntstatus_ok(tctx, status, "2nd src open");
5938
5939         lck.in.lock_count       = 0x0001;
5940         lck.in.lock_sequence    = 0x00000000;
5941         lck.in.file.handle      = src_h2;
5942         lck.in.locks            = el;
5943         el[0].offset            = dup_ext_buf.source_off;
5944         el[0].length            = dup_ext_buf.byte_count;
5945         el[0].reserved          = 0;
5946         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
5947
5948         status = smb2_lock(tree, &lck);
5949         torture_assert_ntstatus_ok(tctx, status, "lock");
5950
5951         status = smb2_util_write(tree, src_h,
5952                                  "conflicted", 0, sizeof("conflicted"));
5953         torture_assert_ntstatus_equal(tctx, status,
5954                                 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
5955
5956         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
5957                                        &dup_ext_buf,
5958                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
5959         torture_assert_ndr_success(tctx, ndr_ret,
5960                                    "ndr_push_fsctl_dup_extents_to_file");
5961
5962         /*
5963          * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
5964          * here.
5965          */
5966         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5967         torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
5968
5969         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5970         if (!ok) {
5971                 torture_fail(tctx, "inconsistent file data");
5972         }
5973
5974         lck.in.lock_count       = 0x0001;
5975         lck.in.lock_sequence    = 0x00000001;
5976         lck.in.file.handle      = src_h2;
5977         lck.in.locks            = el;
5978         el[0].offset            = dup_ext_buf.source_off;
5979         el[0].length            = dup_ext_buf.byte_count;
5980         el[0].reserved          = 0;
5981         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
5982         status = smb2_lock(tree, &lck);
5983         torture_assert_ntstatus_ok(tctx, status, "unlock");
5984
5985         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
5986         torture_assert_ntstatus_ok(tctx, status,
5987                                    "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
5988
5989         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
5990         if (!ok) {
5991                 torture_fail(tctx, "inconsistent file data");
5992         }
5993
5994         smb2_util_close(tree, src_h2);
5995         smb2_util_close(tree, src_h);
5996         smb2_util_close(tree, dest_h);
5997         talloc_free(tmp_ctx);
5998         return true;
5999 }
6000
6001 static bool test_ioctl_dup_extents_dest_lck(struct torture_context *tctx,
6002                                             struct smb2_tree *tree)
6003 {
6004         struct smb2_handle src_h;
6005         struct smb2_handle dest_h;
6006         struct smb2_handle dest_h2;
6007         NTSTATUS status;
6008         union smb_ioctl ioctl;
6009         TALLOC_CTX *tmp_ctx = talloc_new(tree);
6010         struct fsctl_dup_extents_to_file dup_ext_buf;
6011         enum ndr_err_code ndr_ret;
6012         bool ok;
6013         struct smb2_lock lck;
6014         struct smb2_lock_element el[1];
6015
6016         ok = test_setup_dup_extents(tctx, tree, tmp_ctx,
6017                                     &src_h, 32768, /* fill 32768 byte src file */
6018                                     SEC_RIGHTS_FILE_ALL,
6019                                     &dest_h, 0,
6020                                     SEC_RIGHTS_FILE_ALL,
6021                                     &dup_ext_buf,
6022                                     &ioctl);
6023         if (!ok) {
6024                 torture_fail(tctx, "setup dup extents error");
6025         }
6026
6027         status = test_ioctl_fs_supported(tctx, tree, tmp_ctx, &src_h,
6028                                          FILE_SUPPORTS_BLOCK_REFCOUNTING, &ok);
6029         torture_assert_ntstatus_ok(tctx, status, "SMB2_GETINFO_FS");
6030         if (!ok) {
6031                 smb2_util_close(tree, src_h);
6032                 smb2_util_close(tree, dest_h);
6033                 talloc_free(tmp_ctx);
6034                 torture_skip(tctx, "block refcounting not supported\n");
6035         }
6036
6037         /* dest pattern is different to src */
6038         ok = write_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 32768);
6039         torture_assert(tctx, ok, "write pattern");
6040
6041         /* setup dup ext req, values used for locking */
6042         dup_ext_buf.source_off = 0;
6043         dup_ext_buf.target_off = 0;
6044         dup_ext_buf.byte_count = 32768;
6045
6046         /* open and lock the dup extents dest file */
6047         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
6048         torture_assert_ntstatus_ok(tctx, status, "2nd src open");
6049
6050         lck.in.lock_count       = 0x0001;
6051         lck.in.lock_sequence    = 0x00000000;
6052         lck.in.file.handle      = dest_h2;
6053         lck.in.locks            = el;
6054         el[0].offset            = dup_ext_buf.source_off;
6055         el[0].length            = dup_ext_buf.byte_count;
6056         el[0].reserved          = 0;
6057         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
6058
6059         status = smb2_lock(tree, &lck);
6060         torture_assert_ntstatus_ok(tctx, status, "lock");
6061
6062         status = smb2_util_write(tree, dest_h,
6063                                  "conflicted", 0, sizeof("conflicted"));
6064         torture_assert_ntstatus_equal(tctx, status,
6065                                 NT_STATUS_FILE_LOCK_CONFLICT, "file write");
6066
6067         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
6068                                        &dup_ext_buf,
6069                        (ndr_push_flags_fn_t)ndr_push_fsctl_dup_extents_to_file);
6070         torture_assert_ndr_success(tctx, ndr_ret,
6071                                    "ndr_push_fsctl_dup_extents_to_file");
6072
6073         /*
6074          * In contrast to copy-chunk, dup extents doesn't cause a lock conflict
6075          * here.
6076          */
6077         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6078         torture_assert_ntstatus_ok(tctx, status, "FSCTL_DUP_EXTENTS_TO_FILE");
6079
6080         lck.in.lock_count       = 0x0001;
6081         lck.in.lock_sequence    = 0x00000001;
6082         lck.in.file.handle      = dest_h2;
6083         lck.in.locks            = el;
6084         el[0].offset            = dup_ext_buf.source_off;
6085         el[0].length            = dup_ext_buf.byte_count;
6086         el[0].reserved          = 0;
6087         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
6088         status = smb2_lock(tree, &lck);
6089         torture_assert_ntstatus_ok(tctx, status, "unlock");
6090
6091         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
6092         torture_assert_ntstatus_ok(tctx, status,
6093                                    "FSCTL_DUP_EXTENTS_TO_FILE unlocked");
6094
6095         ok = check_pattern(tctx, tree, tmp_ctx, dest_h, 0, 32768, 0);
6096         if (!ok) {
6097                 torture_fail(tctx, "inconsistent file data");
6098         }
6099
6100         smb2_util_close(tree, src_h);
6101         smb2_util_close(tree, dest_h);
6102         smb2_util_close(tree, dest_h2);
6103         talloc_free(tmp_ctx);
6104         return true;
6105 }
6106
6107 /*
6108  * testing of SMB2 ioctls
6109  */
6110 struct torture_suite *torture_smb2_ioctl_init(void)
6111 {
6112         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
6113
6114         torture_suite_add_1smb2_test(suite, "shadow_copy",
6115                                      test_ioctl_get_shadow_copy);
6116         torture_suite_add_1smb2_test(suite, "req_resume_key",
6117                                      test_ioctl_req_resume_key);
6118         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
6119                                      test_ioctl_copy_chunk_simple);
6120         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
6121                                      test_ioctl_copy_chunk_multi);
6122         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
6123                                      test_ioctl_copy_chunk_tiny);
6124         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
6125                                      test_ioctl_copy_chunk_over);
6126         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
6127                                      test_ioctl_copy_chunk_append);
6128         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
6129                                      test_ioctl_copy_chunk_limits);
6130         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
6131                                      test_ioctl_copy_chunk_src_lck);
6132         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
6133                                      test_ioctl_copy_chunk_dest_lck);
6134         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
6135                                      test_ioctl_copy_chunk_bad_key);
6136         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
6137                                      test_ioctl_copy_chunk_src_is_dest);
6138         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
6139                                      test_ioctl_copy_chunk_src_is_dest_overlap);
6140         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
6141                                      test_ioctl_copy_chunk_bad_access);
6142         torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
6143                                      test_ioctl_copy_chunk_write_access);
6144         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
6145                                      test_ioctl_copy_chunk_src_exceed);
6146         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
6147                                      test_ioctl_copy_chunk_src_exceed_multi);
6148         torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
6149                                      test_ioctl_copy_chunk_sparse_dest);
6150         torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
6151                                      test_ioctl_copy_chunk_max_output_sz);
6152         torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
6153                                      test_ioctl_copy_chunk_zero_length);
6154         torture_suite_add_1smb2_test(suite, "compress_file_flag",
6155                                      test_ioctl_compress_file_flag);
6156         torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
6157                                      test_ioctl_compress_dir_inherit);
6158         torture_suite_add_1smb2_test(suite, "compress_invalid_format",
6159                                      test_ioctl_compress_invalid_format);
6160         torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
6161                                      test_ioctl_compress_invalid_buf);
6162         torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
6163                                      test_ioctl_compress_query_file_attr);
6164         torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
6165                                      test_ioctl_compress_create_with_attr);
6166         torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
6167                                      test_ioctl_compress_inherit_disable);
6168         torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
6169                                      test_ioctl_compress_set_file_attr);
6170         torture_suite_add_1smb2_test(suite, "compress_perms",
6171                                      test_ioctl_compress_perms);
6172         torture_suite_add_1smb2_test(suite, "network_interface_info",
6173                                      test_ioctl_network_interface_info);
6174         torture_suite_add_1smb2_test(suite, "sparse_file_flag",
6175                                      test_ioctl_sparse_file_flag);
6176         torture_suite_add_1smb2_test(suite, "sparse_file_attr",
6177                                      test_ioctl_sparse_file_attr);
6178         torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
6179                                      test_ioctl_sparse_dir_flag);
6180         torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
6181                                      test_ioctl_sparse_set_nobuf);
6182         torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
6183                                      test_ioctl_sparse_set_oversize);
6184         torture_suite_add_1smb2_test(suite, "sparse_qar",
6185                                      test_ioctl_sparse_qar);
6186         torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
6187                                      test_ioctl_sparse_qar_malformed);
6188         torture_suite_add_1smb2_test(suite, "sparse_punch",
6189                                      test_ioctl_sparse_punch);
6190         torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
6191                                      test_ioctl_sparse_hole_dealloc);
6192         torture_suite_add_1smb2_test(suite, "sparse_compressed",
6193                                      test_ioctl_sparse_compressed);
6194         torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
6195                                      test_ioctl_sparse_copy_chunk);
6196         torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
6197                                      test_ioctl_sparse_punch_invalid);
6198         torture_suite_add_1smb2_test(suite, "sparse_perms",
6199                                      test_ioctl_sparse_perms);
6200         torture_suite_add_1smb2_test(suite, "sparse_lock",
6201                                      test_ioctl_sparse_lck);
6202         torture_suite_add_1smb2_test(suite, "sparse_qar_ob1",
6203                                      test_ioctl_sparse_qar_ob1);
6204         torture_suite_add_1smb2_test(suite, "sparse_qar_multi",
6205                                      test_ioctl_sparse_qar_multi);
6206         torture_suite_add_1smb2_test(suite, "sparse_qar_overflow",
6207                                      test_ioctl_sparse_qar_overflow);
6208         torture_suite_add_1smb2_test(suite, "trim_simple",
6209                                      test_ioctl_trim_simple);
6210         torture_suite_add_1smb2_test(suite, "dup_extents_simple",
6211                                      test_ioctl_dup_extents_simple);
6212         torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_dest",
6213                                      test_ioctl_dup_extents_len_beyond_dest);
6214         torture_suite_add_1smb2_test(suite, "dup_extents_len_beyond_src",
6215                                      test_ioctl_dup_extents_len_beyond_src);
6216         torture_suite_add_1smb2_test(suite, "dup_extents_len_zero",
6217                                      test_ioctl_dup_extents_len_zero);
6218         torture_suite_add_1smb2_test(suite, "dup_extents_sparse_src",
6219                                      test_ioctl_dup_extents_sparse_src);
6220         torture_suite_add_1smb2_test(suite, "dup_extents_sparse_dest",
6221                                      test_ioctl_dup_extents_sparse_dest);
6222         torture_suite_add_1smb2_test(suite, "dup_extents_sparse_both",
6223                                      test_ioctl_dup_extents_sparse_both);
6224         torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest",
6225                                      test_ioctl_dup_extents_src_is_dest);
6226         torture_suite_add_1smb2_test(suite, "dup_extents_src_is_dest_overlap",
6227                                      test_ioctl_dup_extents_src_is_dest_overlap);
6228         torture_suite_add_1smb2_test(suite, "dup_extents_compressed_src",
6229                                      test_ioctl_dup_extents_compressed_src);
6230         torture_suite_add_1smb2_test(suite, "dup_extents_compressed_dest",
6231                                      test_ioctl_dup_extents_compressed_dest);
6232         torture_suite_add_1smb2_test(suite, "dup_extents_bad_handle",
6233                                      test_ioctl_dup_extents_bad_handle);
6234         torture_suite_add_1smb2_test(suite, "dup_extents_src_lock",
6235                                      test_ioctl_dup_extents_src_lck);
6236         torture_suite_add_1smb2_test(suite, "dup_extents_dest_lock",
6237                                      test_ioctl_dup_extents_dest_lck);
6238
6239         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
6240
6241         return suite;
6242 }
6243