registry: skip root check when running with uid-wrapper enabled
[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 /*
3239  * Manually test setting and clearing sparse flag. Intended for file system
3240  * specifc tests to toggle the flag through SMB and check the status in the
3241  * file system.
3242  */
3243 bool test_ioctl_set_sparse(struct torture_context *tctx)
3244 {
3245         bool set, ret = true;
3246         const char *filename = NULL;
3247         struct smb2_create create = { };
3248         struct smb2_tree *tree = NULL;
3249         NTSTATUS status;
3250
3251         set = torture_setting_bool(tctx, "set_sparse", true);
3252         filename = torture_setting_string(tctx, "filename", NULL);
3253
3254         if (filename == NULL) {
3255                 torture_fail(tctx, "Need to provide filename through "
3256                              "--option=torture:filename=testfile\n");
3257                 return false;
3258         }
3259
3260         if (!torture_smb2_connection(tctx, &tree)) {
3261                 torture_comment(tctx, "Initializing smb2 connection failed.\n");
3262                 return false;
3263         }
3264
3265         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
3266         create.in.create_options = 0;
3267         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
3268         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
3269                 NTCREATEX_SHARE_ACCESS_WRITE |
3270                 NTCREATEX_SHARE_ACCESS_DELETE;
3271         create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
3272         create.in.fname = filename;
3273
3274         status = smb2_create(tree, tctx, &create);
3275         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3276                                         "CREATE failed.\n");
3277
3278         status = test_ioctl_sparse_req(tctx, tctx, tree,
3279                                        create.out.file.handle, set);
3280         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3281                                         "FSCTL_SET_SPARSE failed.\n");
3282 done:
3283
3284         return ret;
3285 }
3286
3287 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
3288                                         struct smb2_tree *tree)
3289 {
3290         struct smb2_handle fh;
3291         union smb_fileinfo io;
3292         NTSTATUS status;
3293         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3294         bool ok;
3295         bool is_sparse;
3296
3297         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3298                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3299                                     FILE_ATTRIBUTE_NORMAL);
3300         torture_assert(torture, ok, "setup file");
3301
3302         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3303                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3304         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3305         if (!ok) {
3306                 smb2_util_close(tree, fh);
3307                 torture_skip(torture, "Sparse files not supported\n");
3308         }
3309
3310         ZERO_STRUCT(io);
3311         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3312         io.generic.in.file.handle = fh;
3313         status = smb2_getinfo_file(tree, tmp_ctx, &io);
3314         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
3315
3316         torture_assert(torture,
3317                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
3318                        "sparse attr before set");
3319
3320         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3321         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3322
3323         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3324         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3325         torture_assert(torture, is_sparse, "no sparse attr after set");
3326
3327         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3328         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3329
3330         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3331         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3332         torture_assert(torture, !is_sparse, "sparse attr after unset");
3333
3334         smb2_util_close(tree, fh);
3335         talloc_free(tmp_ctx);
3336         return true;
3337 }
3338
3339 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
3340                                         struct smb2_tree *tree)
3341 {
3342         struct smb2_handle fh;
3343         NTSTATUS status;
3344         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3345         bool ok;
3346         bool is_sparse;
3347
3348         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3349                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3350                         (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
3351         torture_assert(torture, ok, "setup file");
3352
3353         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3354                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3355         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3356         if (!ok) {
3357                 smb2_util_close(tree, fh);
3358                 torture_skip(torture, "Sparse files not supported\n");
3359         }
3360
3361         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3362         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3363         torture_assert(torture, !is_sparse, "sparse attr on open");
3364
3365         smb2_util_close(tree, fh);
3366         talloc_free(tmp_ctx);
3367         return true;
3368 }
3369
3370 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
3371                                         struct smb2_tree *tree)
3372 {
3373         struct smb2_handle dirh;
3374         NTSTATUS status;
3375         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3376         bool ok;
3377
3378         smb2_deltree(tree, DNAME);
3379         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3380                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
3381                                     FILE_ATTRIBUTE_DIRECTORY);
3382         torture_assert(torture, ok, "setup sparse directory");
3383
3384         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &dirh,
3385                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3386         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3387         if (!ok) {
3388                 smb2_util_close(tree, dirh);
3389                 smb2_deltree(tree, DNAME);
3390                 torture_skip(torture, "Sparse files not supported\n");
3391         }
3392
3393         /* set sparse dir should fail, check for 2k12 & 2k8 response */
3394         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
3395         torture_assert_ntstatus_equal(torture, status,
3396                                       NT_STATUS_INVALID_PARAMETER,
3397                                       "dir FSCTL_SET_SPARSE status");
3398
3399         smb2_util_close(tree, dirh);
3400         smb2_deltree(tree, DNAME);
3401         talloc_free(tmp_ctx);
3402         return true;
3403 }
3404
3405 /*
3406  * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
3407  * buffer to indicate whether the flag should be set or cleared. When sent
3408  * without a buffer, it must be handled as if SetSparse=TRUE.
3409  */
3410 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
3411                                         struct smb2_tree *tree)
3412 {
3413         struct smb2_handle fh;
3414         union smb_ioctl ioctl;
3415         NTSTATUS status;
3416         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3417         bool ok;
3418         bool is_sparse;
3419
3420         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3421                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3422                                     FILE_ATTRIBUTE_NORMAL);
3423         torture_assert(torture, ok, "setup file");
3424
3425         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3426                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3427         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3428         if (!ok) {
3429                 smb2_util_close(tree, fh);
3430                 torture_skip(torture, "Sparse files not supported\n");
3431         }
3432
3433         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3434         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3435         torture_assert(torture, !is_sparse, "sparse attr before set");
3436
3437         ZERO_STRUCT(ioctl);
3438         ioctl.smb2.level = RAW_IOCTL_SMB2;
3439         ioctl.smb2.in.file.handle = fh;
3440         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3441         ioctl.smb2.in.max_output_response = 0;
3442         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3443         /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
3444
3445         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3446         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3447
3448         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3449         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3450         torture_assert(torture, is_sparse, "no sparse attr after set");
3451
3452         /* second non-SetSparse request shouldn't toggle sparse */
3453         ZERO_STRUCT(ioctl);
3454         ioctl.smb2.level = RAW_IOCTL_SMB2;
3455         ioctl.smb2.in.file.handle = fh;
3456         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3457         ioctl.smb2.in.max_output_response = 0;
3458         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3459
3460         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3461         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3462
3463         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3464         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3465         torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
3466
3467         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3468         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3469
3470         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3471         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3472         torture_assert(torture, !is_sparse, "sparse attr after unset");
3473
3474         smb2_util_close(tree, fh);
3475         talloc_free(tmp_ctx);
3476         return true;
3477 }
3478
3479 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
3480                                            struct smb2_tree *tree)
3481 {
3482         struct smb2_handle fh;
3483         union smb_ioctl ioctl;
3484         NTSTATUS status;
3485         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3486         bool ok;
3487         bool is_sparse;
3488         uint8_t buf[100];
3489
3490         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3491                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3492                                     FILE_ATTRIBUTE_NORMAL);
3493         torture_assert(torture, ok, "setup file");
3494
3495         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3496                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3497         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3498         if (!ok) {
3499                 smb2_util_close(tree, fh);
3500                 torture_skip(torture, "Sparse files not supported\n");
3501         }
3502
3503         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3504         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3505         torture_assert(torture, !is_sparse, "sparse attr before set");
3506
3507         ZERO_STRUCT(ioctl);
3508         ioctl.smb2.level = RAW_IOCTL_SMB2;
3509         ioctl.smb2.in.file.handle = fh;
3510         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3511         ioctl.smb2.in.max_output_response = 0;
3512         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3513
3514         /*
3515          * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
3516          * Windows still successfully processes the request.
3517          */
3518         ZERO_ARRAY(buf);
3519         buf[0] = 0xFF; /* attempt to set sparse */
3520         ioctl.smb2.in.out.data = buf;
3521         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3522
3523         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3524         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3525
3526         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3527         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3528         torture_assert(torture, is_sparse, "no sparse attr after set");
3529
3530         ZERO_STRUCT(ioctl);
3531         ioctl.smb2.level = RAW_IOCTL_SMB2;
3532         ioctl.smb2.in.file.handle = fh;
3533         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
3534         ioctl.smb2.in.max_output_response = 0;
3535         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3536
3537         ZERO_ARRAY(buf); /* clear sparse */
3538         ioctl.smb2.in.out.data = buf;
3539         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
3540
3541         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3542         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3543
3544         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3545         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3546         torture_assert(torture, !is_sparse, "sparse attr after clear");
3547
3548         smb2_util_close(tree, fh);
3549         talloc_free(tmp_ctx);
3550         return true;
3551 }
3552
3553 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
3554                                    TALLOC_CTX *mem_ctx,
3555                                    struct smb2_tree *tree,
3556                                    struct smb2_handle fh,
3557                                    int64_t req_off,
3558                                    int64_t req_len,
3559                                    struct file_alloced_range_buf **_rsp,
3560                                    uint64_t *_rsp_count)
3561 {
3562         union smb_ioctl ioctl;
3563         NTSTATUS status;
3564         enum ndr_err_code ndr_ret;
3565         struct file_alloced_range_buf far_buf;
3566         struct file_alloced_range_buf *far_rsp = NULL;
3567         uint64_t far_count = 0;
3568         int i;
3569         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3570         if (tmp_ctx == NULL) {
3571                 return NT_STATUS_NO_MEMORY;
3572         }
3573
3574         ZERO_STRUCT(ioctl);
3575         ioctl.smb2.level = RAW_IOCTL_SMB2;
3576         ioctl.smb2.in.file.handle = fh;
3577         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3578         ioctl.smb2.in.max_output_response = 1024;
3579         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3580
3581         far_buf.file_off = req_off;
3582         far_buf.len = req_len;
3583
3584         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3585                                        &far_buf,
3586                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3587         if (ndr_ret != NDR_ERR_SUCCESS) {
3588                 status = NT_STATUS_UNSUCCESSFUL;
3589                 goto err_out;
3590         }
3591
3592         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3593         if (!NT_STATUS_IS_OK(status)) {
3594                 goto err_out;
3595         }
3596
3597         if (ioctl.smb2.out.out.length == 0) {
3598                 goto done;
3599         }
3600
3601         if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
3602                 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
3603                                 ioctl.smb2.out.out.length);
3604                 status = NT_STATUS_INVALID_VIEW_SIZE;
3605                 goto err_out;
3606         }
3607
3608         far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
3609         far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
3610                                far_count);
3611         if (far_rsp == NULL) {
3612                 status = NT_STATUS_NO_MEMORY;
3613                 goto err_out;
3614         }
3615
3616         for (i = 0; i < far_count; i++) {
3617                 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3618                                                &far_rsp[i],
3619                         (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
3620                 if (ndr_ret != NDR_ERR_SUCCESS) {
3621                         status = NT_STATUS_UNSUCCESSFUL;
3622                         goto err_out;
3623                 }
3624                 /* move to next buffer */
3625                 ioctl.smb2.out.out.data += sizeof(far_buf);
3626                 ioctl.smb2.out.out.length -= sizeof(far_buf);
3627         }
3628
3629 done:
3630         *_rsp = far_rsp;
3631         *_rsp_count = far_count;
3632         status = NT_STATUS_OK;
3633 err_out:
3634         talloc_free(tmp_ctx);
3635         return status;
3636 }
3637
3638 static bool test_ioctl_sparse_qar(struct torture_context *torture,
3639                                   struct smb2_tree *tree)
3640 {
3641         struct smb2_handle fh;
3642         NTSTATUS status;
3643         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3644         bool ok;
3645         bool is_sparse;
3646         struct file_alloced_range_buf *far_rsp = NULL;
3647         uint64_t far_count = 0;
3648
3649         /* zero length file, shouldn't have any ranges */
3650         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3651                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3652                                     FILE_ATTRIBUTE_NORMAL);
3653         torture_assert(torture, ok, "setup file");
3654
3655         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3656                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3657         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3658         if (!ok) {
3659                 smb2_util_close(tree, fh);
3660                 torture_skip(torture, "Sparse files not supported\n");
3661         }
3662
3663         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3664         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3665         torture_assert(torture, !is_sparse, "sparse attr before set");
3666
3667         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3668                                     0,  /* off */
3669                                     0,  /* len */
3670                                     &far_rsp,
3671                                     &far_count);
3672         torture_assert_ntstatus_ok(torture, status,
3673                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3674         torture_assert_u64_equal(torture, far_count, 0,
3675                                  "unexpected response len");
3676
3677         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3678                                     0,  /* off */
3679                                     1024,       /* len */
3680                                     &far_rsp,
3681                                     &far_count);
3682         torture_assert_ntstatus_ok(torture, status,
3683                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3684         torture_assert_u64_equal(torture, far_count, 0,
3685                                  "unexpected response len");
3686
3687         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3688         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3689
3690         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3691         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3692         torture_assert(torture, is_sparse, "no sparse attr after set");
3693
3694         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3695                                     0,  /* off */
3696                                     1024,       /* len */
3697                                     &far_rsp,
3698                                     &far_count);
3699         torture_assert_ntstatus_ok(torture, status,
3700                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3701         torture_assert_u64_equal(torture, far_count, 0,
3702                                  "unexpected response len");
3703
3704         /* write into the (now) sparse file at 4k offset */
3705         ok = write_pattern(torture, tree, tmp_ctx, fh,
3706                            4096,        /* off */
3707                            1024,        /* len */
3708                            4096);       /* pattern offset */
3709         torture_assert(torture, ok, "write pattern");
3710
3711         /*
3712          * Query range before write off. Whether it's allocated or not is FS
3713          * dependent. NTFS deallocates chunks in 64K increments, but others
3714          * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3715          */
3716         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3717                                     0,  /* off */
3718                                     4096,       /* len */
3719                                     &far_rsp,
3720                                     &far_count);
3721         torture_assert_ntstatus_ok(torture, status,
3722                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3723         if (far_count == 0) {
3724                 torture_comment(torture, "FS deallocated 4K chunk\n");
3725         } else {
3726                 /* expect fully allocated */
3727                 torture_assert_u64_equal(torture, far_count, 1,
3728                                          "unexpected response len");
3729                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3730                 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3731         }
3732
3733         /*
3734          * Query range before and past write, it should be allocated up to the
3735          * end of the write.
3736          */
3737         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3738                                     0,  /* off */
3739                                     8192,       /* len */
3740                                     &far_rsp,
3741                                     &far_count);
3742         torture_assert_ntstatus_ok(torture, status,
3743                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3744         torture_assert_u64_equal(torture, far_count, 1,
3745                                  "unexpected response len");
3746         /* FS dependent */
3747         if (far_rsp[0].file_off == 4096) {
3748                 /* 4K chunk unallocated */
3749                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3750                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3751         } else {
3752                 /* expect fully allocated */
3753                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3754                 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3755         }
3756
3757         smb2_util_close(tree, fh);
3758         talloc_free(tmp_ctx);
3759         return true;
3760 }
3761
3762 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3763                                             struct smb2_tree *tree)
3764 {
3765         struct smb2_handle fh;
3766         union smb_ioctl ioctl;
3767         struct file_alloced_range_buf far_buf;
3768         NTSTATUS status;
3769         enum ndr_err_code ndr_ret;
3770         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3771         bool ok;
3772         size_t old_len;
3773
3774         /* zero length file, shouldn't have any ranges */
3775         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3776                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3777                                     FILE_ATTRIBUTE_NORMAL);
3778         torture_assert(torture, ok, "setup file");
3779
3780         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3781                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3782         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3783         if (!ok) {
3784                 smb2_util_close(tree, fh);
3785                 torture_skip(torture, "Sparse files not supported\n");
3786         }
3787
3788         /* no allocated ranges, no space for range response, should pass */
3789         ZERO_STRUCT(ioctl);
3790         ioctl.smb2.level = RAW_IOCTL_SMB2;
3791         ioctl.smb2.in.file.handle = fh;
3792         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3793         ioctl.smb2.in.max_output_response = 0;
3794         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3795
3796         far_buf.file_off = 0;
3797         far_buf.len = 1024;
3798         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3799                                        &far_buf,
3800                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3801         torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3802
3803         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3804         torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3805
3806         /* write into the file at 4k offset */
3807         ok = write_pattern(torture, tree, tmp_ctx, fh,
3808                            0,           /* off */
3809                            1024,        /* len */
3810                            0);          /* pattern offset */
3811         torture_assert(torture, ok, "write pattern");
3812
3813         /* allocated range, no space for range response, should fail */
3814         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3815         torture_assert_ntstatus_equal(torture, status,
3816                                       NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3817
3818         /* oversize (2x) file_alloced_range_buf in request, should pass */
3819         ioctl.smb2.in.max_output_response = 1024;
3820         old_len = ioctl.smb2.in.out.length;
3821         ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3822                                (ioctl.smb2.in.out.length * 2));
3823         torture_assert(torture, ok, "2x data buffer");
3824         memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3825                old_len);
3826         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3827         torture_assert_ntstatus_ok(torture, status, "qar too big");
3828
3829         /* no file_alloced_range_buf in request, should fail */
3830         data_blob_free(&ioctl.smb2.in.out);
3831         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3832         torture_assert_ntstatus_equal(torture, status,
3833                                       NT_STATUS_INVALID_PARAMETER, "qar empty");
3834
3835         return true;
3836 }
3837
3838 /*
3839  * 2.3.57 FSCTL_SET_ZERO_DATA Request
3840  *
3841  * How an implementation zeros data within a file is implementation-dependent.
3842  * A file system MAY choose to deallocate regions of disk space that have been
3843  * zeroed.<50>
3844  * <50>
3845  * ... NTFS might deallocate disk space in the file if the file is stored on an
3846  * NTFS volume, and the file is sparse or compressed. It will free any allocated
3847  * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3848  * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3849  * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3850  * deallocated.
3851  */
3852 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3853                                      TALLOC_CTX *mem_ctx,
3854                                      struct smb2_tree *tree,
3855                                      struct smb2_handle fh,
3856                                      int64_t off,
3857                                      int64_t beyond_final_zero)
3858 {
3859         union smb_ioctl ioctl;
3860         NTSTATUS status;
3861         enum ndr_err_code ndr_ret;
3862         struct file_zero_data_info zdata_info;
3863         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3864         if (tmp_ctx == NULL) {
3865                 return NT_STATUS_NO_MEMORY;
3866         }
3867
3868         ZERO_STRUCT(ioctl);
3869         ioctl.smb2.level = RAW_IOCTL_SMB2;
3870         ioctl.smb2.in.file.handle = fh;
3871         ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3872         ioctl.smb2.in.max_output_response = 0;
3873         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3874
3875         zdata_info.file_off = off;
3876         zdata_info.beyond_final_zero = beyond_final_zero;
3877
3878         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3879                                        &zdata_info,
3880                         (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3881         if (ndr_ret != NDR_ERR_SUCCESS) {
3882                 status = NT_STATUS_UNSUCCESSFUL;
3883                 goto err_out;
3884         }
3885
3886         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3887         if (!NT_STATUS_IS_OK(status)) {
3888                 goto err_out;
3889         }
3890
3891         status = NT_STATUS_OK;
3892 err_out:
3893         talloc_free(tmp_ctx);
3894         return status;
3895 }
3896
3897 bool test_ioctl_zero_data(struct torture_context *tctx)
3898 {
3899         bool ret = true;
3900         int offset, beyond_final_zero;
3901         const char *filename;
3902         NTSTATUS status;
3903         struct smb2_create create = { };
3904         struct smb2_tree *tree = NULL;
3905
3906         offset = torture_setting_int(tctx, "offset", -1);
3907
3908         if (offset < 0) {
3909                 torture_fail(tctx, "Need to provide non-negative offset "
3910                              "through --option=torture:offset=NNN\n");
3911                 return false;
3912         }
3913
3914         beyond_final_zero = torture_setting_int(tctx, "beyond_final_zero",
3915                                                 -1);
3916         if (beyond_final_zero < 0) {
3917                 torture_fail(tctx, "Need to provide non-negative "
3918                              "'beyond final zero' through "
3919                              "--option=torture:beyond_final_zero=NNN\n");
3920                 return false;
3921         }
3922         filename = torture_setting_string(tctx, "filename", NULL);
3923         if (filename == NULL) {
3924                 torture_fail(tctx, "Need to provide filename through "
3925                              "--option=torture:filename=testfile\n");
3926                 return false;
3927         }
3928
3929         if (!torture_smb2_connection(tctx, &tree)) {
3930                 torture_comment(tctx, "Initializing smb2 connection failed.\n");
3931                 return false;
3932         }
3933
3934         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
3935         create.in.create_options = 0;
3936         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
3937         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
3938                 NTCREATEX_SHARE_ACCESS_WRITE |
3939                 NTCREATEX_SHARE_ACCESS_DELETE;
3940         create.in.create_disposition = NTCREATEX_DISP_OPEN;
3941         create.in.fname = filename;
3942
3943         status = smb2_create(tree, tctx, &create);
3944         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3945                                         "CREATE failed.\n");
3946
3947         status = test_ioctl_zdata_req(tctx, tctx, tree,
3948                                       create.out.file.handle,
3949                                       offset,
3950                                       beyond_final_zero);
3951         torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
3952                                         "FSCTL_ZERO_DATA failed.\n");
3953
3954 done:
3955         return ret;
3956 }
3957
3958 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3959                                     struct smb2_tree *tree)
3960 {
3961         struct smb2_handle fh;
3962         NTSTATUS status;
3963         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3964         bool ok;
3965         bool is_sparse;
3966         struct file_alloced_range_buf *far_rsp = NULL;
3967         uint64_t far_count = 0;
3968
3969         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3970                                     FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3971                                     FILE_ATTRIBUTE_NORMAL);
3972         torture_assert(torture, ok, "setup file");
3973
3974         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
3975                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
3976         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3977         if (!ok) {
3978                 smb2_util_close(tree, fh);
3979                 torture_skip(torture, "Sparse files not supported\n");
3980         }
3981
3982         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3983         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3984         torture_assert(torture, !is_sparse, "sparse attr before set");
3985
3986         /* zero (hole-punch) the data, without sparse flag */
3987         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3988                                       0,        /* off */
3989                                       4096);    /* beyond_final_zero */
3990         torture_assert_ntstatus_ok(torture, status, "zero_data");
3991
3992         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3993                                     0,          /* off */
3994                                     4096,       /* len */
3995                                     &far_rsp,
3996                                     &far_count);
3997         torture_assert_ntstatus_ok(torture, status,
3998                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3999         torture_assert_u64_equal(torture, far_count, 1,
4000                                  "unexpected response len");
4001
4002         /* expect fully allocated */
4003         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4004                                  "unexpected far off");
4005         torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4006                                  "unexpected far len");
4007         /* check that the data is now zeroed */
4008         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4009         torture_assert(torture, ok, "non-sparse zeroed range");
4010
4011         /* set sparse */
4012         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4013         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4014
4015         /* still fully allocated on NTFS, see note below for Samba */
4016         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4017                                     0,          /* off */
4018                                     4096,       /* len */
4019                                     &far_rsp,
4020                                     &far_count);
4021         torture_assert_ntstatus_ok(torture, status,
4022                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4023         /*
4024          * FS specific: Samba uses PUNCH_HOLE to zero the range, and
4025          * subsequently uses fallocate() to allocate the punched range if the
4026          * file is marked non-sparse and "strict allocate" is enabled. In both
4027          * cases, the zeroed range will not be detected by SEEK_DATA, so the
4028          * range won't be present in QAR responses until the file is marked
4029          * non-sparse again.
4030          */
4031         if (far_count == 0) {
4032                 torture_comment(torture, "non-sparse zeroed range disappeared "
4033                                 "after marking sparse\n");
4034         } else {
4035                 /* NTFS: range remains fully allocated */
4036                 torture_assert_u64_equal(torture, far_count, 1,
4037                                          "unexpected response len");
4038                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4039                                          "unexpected far off");
4040                 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4041                                          "unexpected far len");
4042         }
4043
4044         /* zero (hole-punch) the data, _with_ sparse flag */
4045         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4046                                       0,        /* off */
4047                                       4096);    /* beyond_final_zero */
4048         torture_assert_ntstatus_ok(torture, status, "zero_data");
4049
4050         /* the range should no longer be alloced */
4051         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4052                                     0,          /* off */
4053                                     4096,       /* len */
4054                                     &far_rsp,
4055                                     &far_count);
4056         torture_assert_ntstatus_ok(torture, status,
4057                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4058         torture_assert_u64_equal(torture, far_count, 0,
4059                                  "unexpected response len");
4060
4061         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4062         torture_assert(torture, ok, "sparse zeroed range");
4063
4064         /* remove sparse flag, this should "unsparse" the zeroed range */
4065         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
4066         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4067
4068         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4069                                     0,          /* off */
4070                                     4096,       /* len */
4071                                     &far_rsp,
4072                                     &far_count);
4073         torture_assert_ntstatus_ok(torture, status,
4074                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4075         torture_assert_u64_equal(torture, far_count, 1,
4076                                  "unexpected response len");
4077         /* expect fully allocated */
4078         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4079                                  "unexpected far off");
4080         torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
4081                                  "unexpected far len");
4082
4083         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
4084         torture_assert(torture, ok, "sparse zeroed range");
4085
4086         smb2_util_close(tree, fh);
4087         talloc_free(tmp_ctx);
4088         return true;
4089 }
4090
4091 /*
4092  * Find the point at which a zeroed range in a sparse file is deallocated by the
4093  * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
4094  * increments. Also check whether zeroed neighbours are merged for deallocation.
4095  */
4096 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
4097                                            struct smb2_tree *tree)
4098 {
4099         struct smb2_handle fh;
4100         NTSTATUS status;
4101         TALLOC_CTX *tmp_ctx = talloc_new(tree);
4102         bool ok;
4103         uint64_t file_size;
4104         uint64_t hlen;
4105         uint64_t dealloc_chunk_len = 0;
4106         struct file_alloced_range_buf *far_rsp = NULL;
4107         uint64_t far_count = 0;
4108
4109         ok = test_setup_create_fill(torture, tree, tmp_ctx,
4110                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
4111                                     FILE_ATTRIBUTE_NORMAL);
4112         torture_assert(torture, ok, "setup file 1");
4113
4114         /* check for FS sparse file */
4115         status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
4116                                          FILE_SUPPORTS_SPARSE_FILES, &ok);
4117         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
4118         if (!ok) {
4119                 smb2_util_close(tree, fh);
4120                 torture_skip(torture, "Sparse files not supported\n");
4121         }
4122
4123         /* set sparse */
4124         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
4125         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4126
4127         file_size = 1024 * 1024;
4128
4129         ok = write_pattern(torture, tree, tmp_ctx, fh,
4130                            0,           /* off */
4131                            file_size,   /* len */
4132                            0);  /* pattern offset */
4133         torture_assert(torture, ok, "write pattern");
4134
4135          /* check allocated ranges, should be fully allocated */
4136         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4137                                     0,                  /* off */
4138                                     file_size,          /* len */
4139                                     &far_rsp,
4140                                     &far_count);
4141         torture_assert_ntstatus_ok(torture, status,
4142                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4143         torture_assert_u64_equal(torture, far_count, 1,
4144                                  "unexpected response len");
4145         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
4146                                  "unexpected far off");
4147         torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
4148                                  "unexpected far len");
4149
4150         /* punch holes in sizes of 1k increments */
4151         for (hlen = 0; hlen <= file_size; hlen += 4096) {
4152
4153                 /* punch a hole from zero to the current increment */
4154                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
4155                                               0,        /* off */
4156                                               hlen);    /* beyond_final_zero */
4157                 torture_assert_ntstatus_ok(torture, status, "zero_data");
4158
4159                 /* ensure hole is zeroed, and pattern is consistent */
4160                 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
4161                 torture_assert(torture, ok, "sparse zeroed range");
4162
4163                 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
4164                                    file_size - hlen, hlen);
4165                 torture_assert(torture, ok, "allocated pattern range");
4166
4167                  /* Check allocated ranges, hole might have been deallocated */
4168                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4169                                             0,          /* off */
4170                                             file_size,  /* len */
4171                                             &far_rsp,
4172                                             &far_count);
4173                 torture_assert_ntstatus_ok(torture, status,
4174                                            "FSCTL_QUERY_ALLOCATED_RANGES");
4175                 if ((hlen == file_size) && (far_count == 0)) {
4176                         /* hole covered entire file, deallocation occurred */
4177                         dealloc_chunk_len = file_size;
4178                         break;
4179                 }
4180
4181                 torture_assert_u64_equal(torture, far_count, 1,
4182                                          "unexpected response len");
4183                 if (far_rsp[0].file_off != 0) {
4184                         /*
4185                          * We now know the hole punch length needed to trigger a