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