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