torture/ioctl: rework and reduce pattern helper IO sizes
[gd/samba-autobuild/.git] / source4 / torture / smb2 / ioctl.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 ioctl operations
5
6    Copyright (C) David Disseldorp 2011-2015
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
30
31 #define FNAME   "testfsctl.dat"
32 #define FNAME2  "testfsctl2.dat"
33 #define DNAME   "testfsctl_dir"
34
35 /*
36    basic testing of SMB2 shadow copy calls
37 */
38 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
39                                        struct smb2_tree *tree)
40 {
41         struct smb2_handle h;
42         uint8_t buf[100];
43         NTSTATUS status;
44         union smb_ioctl ioctl;
45         TALLOC_CTX *tmp_ctx = talloc_new(tree);
46
47         smb2_util_unlink(tree, FNAME);
48
49         status = torture_smb2_testfile(tree, FNAME, &h);
50         torture_assert_ntstatus_ok(torture, status, "create write");
51
52         ZERO_ARRAY(buf);
53         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
54         torture_assert_ntstatus_ok(torture, status, "write");
55
56         ZERO_STRUCT(ioctl);
57         ioctl.smb2.level = RAW_IOCTL_SMB2;
58         ioctl.smb2.in.file.handle = h;
59         ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
60         ioctl.smb2.in.max_response_size = 16;
61         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
62
63         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
64         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
65          || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
66                 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
67         }
68         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
69
70         return true;
71 }
72
73 /*
74    basic testing of the SMB2 server side copy ioctls
75 */
76 static bool test_ioctl_req_resume_key(struct torture_context *torture,
77                                       struct smb2_tree *tree)
78 {
79         struct smb2_handle h;
80         uint8_t buf[100];
81         NTSTATUS status;
82         union smb_ioctl ioctl;
83         TALLOC_CTX *tmp_ctx = talloc_new(tree);
84         struct req_resume_key_rsp res_key;
85         enum ndr_err_code ndr_ret;
86
87         smb2_util_unlink(tree, FNAME);
88
89         status = torture_smb2_testfile(tree, FNAME, &h);
90         torture_assert_ntstatus_ok(torture, status, "create write");
91
92         ZERO_ARRAY(buf);
93         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
94         torture_assert_ntstatus_ok(torture, status, "write");
95
96         ZERO_STRUCT(ioctl);
97         ioctl.smb2.level = RAW_IOCTL_SMB2;
98         ioctl.smb2.in.file.handle = h;
99         ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
100         ioctl.smb2.in.max_response_size = 32;
101         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
102
103         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
104         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
105
106         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
107                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
108         torture_assert_ndr_success(torture, ndr_ret,
109                                    "ndr_pull_req_resume_key_rsp");
110
111         ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
112
113         talloc_free(tmp_ctx);
114         return true;
115 }
116
117 static uint64_t patt_hash(uint64_t off)
118 {
119         return off;
120 }
121
122 static bool write_pattern(struct torture_context *torture,
123                           struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
124                           struct smb2_handle h, uint64_t off, uint64_t len,
125                           uint64_t patt_off)
126 {
127         NTSTATUS status;
128         uint64_t i;
129         uint8_t *buf;
130         uint64_t io_sz = MIN(1024 * 64, len);
131
132         if (len == 0) {
133                 return true;
134         }
135
136         torture_assert(torture, (len % 8) == 0, "invalid write len");
137
138         buf = talloc_zero_size(mem_ctx, io_sz);
139         torture_assert(torture, (buf != NULL), "no memory for file data buf");
140
141         while (len > 0) {
142                 for (i = 0; i <= io_sz - 8; i += 8) {
143                         SBVAL(buf, i, patt_hash(patt_off));
144                         patt_off += 8;
145                 }
146
147                 status = smb2_util_write(tree, h,
148                                          buf, off, io_sz);
149                 torture_assert_ntstatus_ok(torture, status, "file write");
150
151                 len -= io_sz;
152                 off += io_sz;
153         }
154
155         talloc_free(buf);
156
157         return true;
158 }
159
160 static bool check_pattern(struct torture_context *torture,
161                           struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
162                           struct smb2_handle h, uint64_t off, uint64_t len,
163                           uint64_t patt_off)
164 {
165         if (len == 0) {
166                 return true;
167         }
168
169         torture_assert(torture, (len % 8) == 0, "invalid read len");
170
171         while (len > 0) {
172                 uint64_t i;
173                 struct smb2_read r;
174                 NTSTATUS status;
175                 uint64_t io_sz = MIN(1024 * 64, len);
176
177                 ZERO_STRUCT(r);
178                 r.in.file.handle = h;
179                 r.in.length      = io_sz;
180                 r.in.offset      = off;
181                 status = smb2_read(tree, mem_ctx, &r);
182                 torture_assert_ntstatus_ok(torture, status, "read");
183
184                 torture_assert_u64_equal(torture, r.out.data.length, io_sz,
185                                          "read data len mismatch");
186
187                 for (i = 0; i <= io_sz - 8; i += 8, patt_off += 8) {
188                         uint64_t data = BVAL(r.out.data.data, i);
189                         torture_assert_u64_equal(torture, data, patt_hash(patt_off),
190                                                  talloc_asprintf(torture, "read data "
191                                                                  "pattern bad at %llu\n",
192                                                                  (unsigned long long)off + i));
193                 }
194                 talloc_free(r.out.data.data);
195                 len -= io_sz;
196                 off += io_sz;
197         }
198
199         return true;
200 }
201
202 static bool check_zero(struct torture_context *torture,
203                        struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
204                        struct smb2_handle h, uint64_t off, uint64_t len)
205 {
206         uint64_t i;
207         struct smb2_read r;
208         NTSTATUS status;
209
210         if (len == 0) {
211                 return true;
212         }
213
214         ZERO_STRUCT(r);
215         r.in.file.handle = h;
216         r.in.length      = len;
217         r.in.offset      = off;
218         status = smb2_read(tree, mem_ctx, &r);
219         torture_assert_ntstatus_ok(torture, status, "read");
220
221         torture_assert_u64_equal(torture, r.out.data.length, len,
222                                  "read data len mismatch");
223
224         for (i = 0; i <= len - 8; i += 8) {
225                 uint64_t data = BVAL(r.out.data.data, i);
226                 torture_assert_u64_equal(torture, data, 0,
227                                          talloc_asprintf(mem_ctx, "read zero "
228                                                          "bad at %llu\n",
229                                                          (unsigned long long)i));
230         }
231
232         talloc_free(r.out.data.data);
233         return true;
234 }
235
236 static bool test_setup_open(struct torture_context *torture,
237                             struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
238                             const char *fname,
239                             struct smb2_handle *fh,
240                             uint32_t desired_access,
241                             uint32_t file_attributes)
242 {
243         struct smb2_create io;
244         NTSTATUS status;
245
246         ZERO_STRUCT(io);
247         io.in.desired_access = desired_access;
248         io.in.file_attributes = file_attributes;
249         io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
250         io.in.share_access =
251                 NTCREATEX_SHARE_ACCESS_DELETE|
252                 NTCREATEX_SHARE_ACCESS_READ|
253                 NTCREATEX_SHARE_ACCESS_WRITE;
254         if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
255                 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
256         }
257         io.in.fname = fname;
258
259         status = smb2_create(tree, mem_ctx, &io);
260         torture_assert_ntstatus_ok(torture, status, "file create");
261
262         *fh = io.out.file.handle;
263
264         return true;
265 }
266
267 static bool test_setup_create_fill(struct torture_context *torture,
268                                    struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
269                                    const char *fname,
270                                    struct smb2_handle *fh,
271                                    uint64_t size,
272                                    uint32_t desired_access,
273                                    uint32_t file_attributes)
274 {
275         bool ok;
276
277         smb2_util_unlink(tree, fname);
278
279         ok = test_setup_open(torture, tree, mem_ctx,
280                              fname,
281                              fh,
282                              desired_access,
283                              file_attributes);
284         torture_assert(torture, ok, "file open");
285
286         if (size > 0) {
287                 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
288                 torture_assert(torture, ok, "write pattern");
289         }
290         return true;
291 }
292
293 static bool test_setup_copy_chunk(struct torture_context *torture,
294                                   struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
295                                   uint32_t nchunks,
296                                   struct smb2_handle *src_h,
297                                   uint64_t src_size,
298                                   uint32_t src_desired_access,
299                                   struct smb2_handle *dest_h,
300                                   uint64_t dest_size,
301                                   uint32_t dest_desired_access,
302                                   struct srv_copychunk_copy *cc_copy,
303                                   union smb_ioctl *ioctl)
304 {
305         struct req_resume_key_rsp res_key;
306         bool ok;
307         NTSTATUS status;
308         enum ndr_err_code ndr_ret;
309
310         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
311                                     src_h, src_size, src_desired_access,
312                                     FILE_ATTRIBUTE_NORMAL);
313         torture_assert(torture, ok, "src file create fill");
314
315         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
316                                     dest_h, dest_size, dest_desired_access,
317                                     FILE_ATTRIBUTE_NORMAL);
318         torture_assert(torture, ok, "dest file create fill");
319
320         ZERO_STRUCTPN(ioctl);
321         ioctl->smb2.level = RAW_IOCTL_SMB2;
322         ioctl->smb2.in.file.handle = *src_h;
323         ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
324         /* Allow for Key + ContextLength + Context */
325         ioctl->smb2.in.max_response_size = 32;
326         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
327
328         status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
329         torture_assert_ntstatus_ok(torture, status,
330                                    "FSCTL_SRV_REQUEST_RESUME_KEY");
331
332         ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
333                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
334
335         torture_assert_ndr_success(torture, ndr_ret,
336                                    "ndr_pull_req_resume_key_rsp");
337
338         ZERO_STRUCTPN(ioctl);
339         ioctl->smb2.level = RAW_IOCTL_SMB2;
340         ioctl->smb2.in.file.handle = *dest_h;
341         ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
342         ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
343         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
344
345         ZERO_STRUCTPN(cc_copy);
346         memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
347         cc_copy->chunk_count = nchunks;
348         cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
349         torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
350
351         return true;
352 }
353
354
355 static bool check_copy_chunk_rsp(struct torture_context *torture,
356                                  struct srv_copychunk_rsp *cc_rsp,
357                                  uint32_t ex_chunks_written,
358                                  uint32_t ex_chunk_bytes_written,
359                                  uint32_t ex_total_bytes_written)
360 {
361         torture_assert_int_equal(torture, cc_rsp->chunks_written,
362                                  ex_chunks_written, "num chunks");
363         torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
364                                  ex_chunk_bytes_written, "chunk bytes written");
365         torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
366                                  ex_total_bytes_written, "chunk total bytes");
367         return true;
368 }
369
370 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
371                                          struct smb2_tree *tree)
372 {
373         struct smb2_handle src_h;
374         struct smb2_handle dest_h;
375         NTSTATUS status;
376         union smb_ioctl ioctl;
377         TALLOC_CTX *tmp_ctx = talloc_new(tree);
378         struct srv_copychunk_copy cc_copy;
379         struct srv_copychunk_rsp cc_rsp;
380         enum ndr_err_code ndr_ret;
381         bool ok;
382
383         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
384                                    1, /* 1 chunk */
385                                    &src_h, 4096, /* fill 4096 byte src file */
386                                    SEC_RIGHTS_FILE_ALL,
387                                    &dest_h, 0,  /* 0 byte dest file */
388                                    SEC_RIGHTS_FILE_ALL,
389                                    &cc_copy,
390                                    &ioctl);
391         if (!ok) {
392                 torture_fail(torture, "setup copy chunk error");
393         }
394
395         /* copy all src file data (via a single chunk desc) */
396         cc_copy.chunks[0].source_off = 0;
397         cc_copy.chunks[0].target_off = 0;
398         cc_copy.chunks[0].length = 4096;
399
400         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
401                                        &cc_copy,
402                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
403         torture_assert_ndr_success(torture, ndr_ret,
404                                    "ndr_push_srv_copychunk_copy");
405
406         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
407         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
408
409         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
410                                        &cc_rsp,
411                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
412         torture_assert_ndr_success(torture, ndr_ret,
413                                    "ndr_pull_srv_copychunk_rsp");
414
415         ok = check_copy_chunk_rsp(torture, &cc_rsp,
416                                   1,    /* chunks written */
417                                   0,    /* chunk bytes unsuccessfully written */
418                                   4096); /* total bytes written */
419         if (!ok) {
420                 torture_fail(torture, "bad copy chunk response data");
421         }
422
423         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
424         if (!ok) {
425                 torture_fail(torture, "inconsistent file data");
426         }
427
428         smb2_util_close(tree, src_h);
429         smb2_util_close(tree, dest_h);
430         talloc_free(tmp_ctx);
431         return true;
432 }
433
434 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
435                                         struct smb2_tree *tree)
436 {
437         struct smb2_handle src_h;
438         struct smb2_handle dest_h;
439         NTSTATUS status;
440         union smb_ioctl ioctl;
441         TALLOC_CTX *tmp_ctx = talloc_new(tree);
442         struct srv_copychunk_copy cc_copy;
443         struct srv_copychunk_rsp cc_rsp;
444         enum ndr_err_code ndr_ret;
445         bool ok;
446
447         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
448                                    2, /* chunks */
449                                    &src_h, 8192, /* src file */
450                                    SEC_RIGHTS_FILE_ALL,
451                                    &dest_h, 0,  /* dest file */
452                                    SEC_RIGHTS_FILE_ALL,
453                                    &cc_copy,
454                                    &ioctl);
455         if (!ok) {
456                 torture_fail(torture, "setup copy chunk error");
457         }
458
459         /* copy all src file data via two chunks */
460         cc_copy.chunks[0].source_off = 0;
461         cc_copy.chunks[0].target_off = 0;
462         cc_copy.chunks[0].length = 4096;
463
464         cc_copy.chunks[1].source_off = 4096;
465         cc_copy.chunks[1].target_off = 4096;
466         cc_copy.chunks[1].length = 4096;
467
468         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
469                                        &cc_copy,
470                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
471         torture_assert_ndr_success(torture, ndr_ret,
472                                    "ndr_push_srv_copychunk_copy");
473
474         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
475         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
476
477         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
478                                        &cc_rsp,
479                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
480         torture_assert_ndr_success(torture, ndr_ret,
481                                    "ndr_pull_srv_copychunk_rsp");
482
483         ok = check_copy_chunk_rsp(torture, &cc_rsp,
484                                   2,    /* chunks written */
485                                   0,    /* chunk bytes unsuccessfully written */
486                                   8192);        /* total bytes written */
487         if (!ok) {
488                 torture_fail(torture, "bad copy chunk response data");
489         }
490
491         smb2_util_close(tree, src_h);
492         smb2_util_close(tree, dest_h);
493         talloc_free(tmp_ctx);
494         return true;
495 }
496
497 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
498                                        struct smb2_tree *tree)
499 {
500         struct smb2_handle src_h;
501         struct smb2_handle dest_h;
502         NTSTATUS status;
503         union smb_ioctl ioctl;
504         TALLOC_CTX *tmp_ctx = talloc_new(tree);
505         struct srv_copychunk_copy cc_copy;
506         struct srv_copychunk_rsp cc_rsp;
507         enum ndr_err_code ndr_ret;
508         bool ok;
509
510         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
511                                    2, /* chunks */
512                                    &src_h, 96, /* src file */
513                                    SEC_RIGHTS_FILE_ALL,
514                                    &dest_h, 0,  /* dest file */
515                                    SEC_RIGHTS_FILE_ALL,
516                                    &cc_copy,
517                                    &ioctl);
518         if (!ok) {
519                 torture_fail(torture, "setup copy chunk error");
520         }
521
522         /* copy all src file data via two chunks, sub block size chunks */
523         cc_copy.chunks[0].source_off = 0;
524         cc_copy.chunks[0].target_off = 0;
525         cc_copy.chunks[0].length = 48;
526
527         cc_copy.chunks[1].source_off = 48;
528         cc_copy.chunks[1].target_off = 48;
529         cc_copy.chunks[1].length = 48;
530
531         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
532                                        &cc_copy,
533                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
534         torture_assert_ndr_success(torture, ndr_ret,
535                                    "ndr_push_srv_copychunk_copy");
536
537         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
538         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
539
540         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
541                                        &cc_rsp,
542                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
543         torture_assert_ndr_success(torture, ndr_ret,
544                                    "ndr_pull_srv_copychunk_rsp");
545
546         ok = check_copy_chunk_rsp(torture, &cc_rsp,
547                                   2,    /* chunks written */
548                                   0,    /* chunk bytes unsuccessfully written */
549                                   96);  /* total bytes written */
550         if (!ok) {
551                 torture_fail(torture, "bad copy chunk response data");
552         }
553
554         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 96, 0);
555         if (!ok) {
556                 torture_fail(torture, "inconsistent file data");
557         }
558
559         smb2_util_close(tree, src_h);
560         smb2_util_close(tree, dest_h);
561         talloc_free(tmp_ctx);
562         return true;
563 }
564
565 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
566                                        struct smb2_tree *tree)
567 {
568         struct smb2_handle src_h;
569         struct smb2_handle dest_h;
570         NTSTATUS status;
571         union smb_ioctl ioctl;
572         TALLOC_CTX *tmp_ctx = talloc_new(tree);
573         struct srv_copychunk_copy cc_copy;
574         struct srv_copychunk_rsp cc_rsp;
575         enum ndr_err_code ndr_ret;
576         bool ok;
577
578         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
579                                    2, /* chunks */
580                                    &src_h, 8192, /* src file */
581                                    SEC_RIGHTS_FILE_ALL,
582                                    &dest_h, 4096, /* dest file */
583                                    SEC_RIGHTS_FILE_ALL,
584                                    &cc_copy,
585                                    &ioctl);
586         if (!ok) {
587                 torture_fail(torture, "setup copy chunk error");
588         }
589
590         /* first chunk overwrites existing dest data */
591         cc_copy.chunks[0].source_off = 0;
592         cc_copy.chunks[0].target_off = 0;
593         cc_copy.chunks[0].length = 4096;
594
595         /* second chunk overwrites the first */
596         cc_copy.chunks[1].source_off = 4096;
597         cc_copy.chunks[1].target_off = 0;
598         cc_copy.chunks[1].length = 4096;
599
600         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
601                                        &cc_copy,
602                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
603         torture_assert_ndr_success(torture, ndr_ret,
604                                    "ndr_push_srv_copychunk_copy");
605
606         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
607         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
608
609         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
610                                        &cc_rsp,
611                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
612         torture_assert_ndr_success(torture, ndr_ret,
613                                    "ndr_pull_srv_copychunk_rsp");
614
615         ok = check_copy_chunk_rsp(torture, &cc_rsp,
616                                   2,    /* chunks written */
617                                   0,    /* chunk bytes unsuccessfully written */
618                                   8192); /* total bytes written */
619         if (!ok) {
620                 torture_fail(torture, "bad copy chunk response data");
621         }
622
623         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
624         if (!ok) {
625                 torture_fail(torture, "inconsistent file data");
626         }
627
628         smb2_util_close(tree, src_h);
629         smb2_util_close(tree, dest_h);
630         talloc_free(tmp_ctx);
631         return true;
632 }
633
634 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
635                                        struct smb2_tree *tree)
636 {
637         struct smb2_handle src_h;
638         struct smb2_handle dest_h;
639         NTSTATUS status;
640         union smb_ioctl ioctl;
641         TALLOC_CTX *tmp_ctx = talloc_new(tree);
642         struct srv_copychunk_copy cc_copy;
643         struct srv_copychunk_rsp cc_rsp;
644         enum ndr_err_code ndr_ret;
645         bool ok;
646
647         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
648                                    2, /* chunks */
649                                    &src_h, 4096, /* src file */
650                                    SEC_RIGHTS_FILE_ALL,
651                                    &dest_h, 0,  /* dest file */
652                                    SEC_RIGHTS_FILE_ALL,
653                                    &cc_copy,
654                                    &ioctl);
655         if (!ok) {
656                 torture_fail(torture, "setup copy chunk error");
657         }
658
659         cc_copy.chunks[0].source_off = 0;
660         cc_copy.chunks[0].target_off = 0;
661         cc_copy.chunks[0].length = 4096;
662
663         /* second chunk appends the same data to the first */
664         cc_copy.chunks[1].source_off = 0;
665         cc_copy.chunks[1].target_off = 4096;
666         cc_copy.chunks[1].length = 4096;
667
668         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
669                                        &cc_copy,
670                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
671         torture_assert_ndr_success(torture, ndr_ret,
672                                    "ndr_push_srv_copychunk_copy");
673
674         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
675         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
676
677         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
678                                        &cc_rsp,
679                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
680         torture_assert_ndr_success(torture, ndr_ret,
681                                    "ndr_pull_srv_copychunk_rsp");
682
683         ok = check_copy_chunk_rsp(torture, &cc_rsp,
684                                   2,    /* chunks written */
685                                   0,    /* chunk bytes unsuccessfully written */
686                                   8192); /* total bytes written */
687         if (!ok) {
688                 torture_fail(torture, "bad copy chunk response data");
689         }
690
691         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
692         if (!ok) {
693                 torture_fail(torture, "inconsistent file data");
694         }
695
696         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
697         if (!ok) {
698                 torture_fail(torture, "inconsistent file data");
699         }
700
701         smb2_util_close(tree, src_h);
702         smb2_util_close(tree, dest_h);
703         talloc_free(tmp_ctx);
704         return true;
705 }
706
707 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
708                                          struct smb2_tree *tree)
709 {
710         struct smb2_handle src_h;
711         struct smb2_handle dest_h;
712         NTSTATUS status;
713         union smb_ioctl ioctl;
714         TALLOC_CTX *tmp_ctx = talloc_new(tree);
715         struct srv_copychunk_copy cc_copy;
716         struct srv_copychunk_rsp cc_rsp;
717         enum ndr_err_code ndr_ret;
718         bool ok;
719
720         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
721                                    1, /* chunks */
722                                    &src_h, 4096, /* src file */
723                                    SEC_RIGHTS_FILE_ALL,
724                                    &dest_h, 0,  /* dest file */
725                                    SEC_RIGHTS_FILE_ALL,
726                                    &cc_copy,
727                                    &ioctl);
728         if (!ok) {
729                 torture_fail(torture, "setup copy chunk error");
730         }
731
732         /* send huge chunk length request */
733         cc_copy.chunks[0].source_off = 0;
734         cc_copy.chunks[0].target_off = 0;
735         cc_copy.chunks[0].length = UINT_MAX;
736
737         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
738                                        &cc_copy,
739                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
740         torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
741
742         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
743         torture_assert_ntstatus_equal(torture, status,
744                                       NT_STATUS_INVALID_PARAMETER,
745                                       "bad oversize chunk response");
746
747         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
748                                        &cc_rsp,
749                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
750         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
751
752         torture_comment(torture, "limit max chunks, got %u\n",
753                         cc_rsp.chunks_written);
754         torture_comment(torture, "limit max chunk len, got %u\n",
755                         cc_rsp.chunk_bytes_written);
756         torture_comment(torture, "limit max total bytes, got %u\n",
757                         cc_rsp.total_bytes_written);
758
759         smb2_util_close(tree, src_h);
760         smb2_util_close(tree, dest_h);
761         talloc_free(tmp_ctx);
762         return true;
763 }
764
765 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
766                                           struct smb2_tree *tree)
767 {
768         struct smb2_handle src_h;
769         struct smb2_handle src_h2;
770         struct smb2_handle dest_h;
771         NTSTATUS status;
772         union smb_ioctl ioctl;
773         TALLOC_CTX *tmp_ctx = talloc_new(tree);
774         struct srv_copychunk_copy cc_copy;
775         struct srv_copychunk_rsp cc_rsp;
776         enum ndr_err_code ndr_ret;
777         bool ok;
778         struct smb2_lock lck;
779         struct smb2_lock_element el[1];
780
781         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
782                                    1, /* chunks */
783                                    &src_h, 4096, /* src file */
784                                    SEC_RIGHTS_FILE_ALL,
785                                    &dest_h, 0,  /* dest file */
786                                    SEC_RIGHTS_FILE_ALL,
787                                    &cc_copy,
788                                    &ioctl);
789         if (!ok) {
790                 torture_fail(torture, "setup copy chunk error");
791         }
792
793         cc_copy.chunks[0].source_off = 0;
794         cc_copy.chunks[0].target_off = 0;
795         cc_copy.chunks[0].length = 4096;
796
797         /* open and lock the copychunk src file */
798         status = torture_smb2_testfile(tree, FNAME, &src_h2);
799         torture_assert_ntstatus_ok(torture, status, "2nd src open");
800
801         lck.in.lock_count       = 0x0001;
802         lck.in.lock_sequence    = 0x00000000;
803         lck.in.file.handle      = src_h2;
804         lck.in.locks            = el;
805         el[0].offset            = cc_copy.chunks[0].source_off;
806         el[0].length            = cc_copy.chunks[0].length;
807         el[0].reserved          = 0;
808         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
809
810         status = smb2_lock(tree, &lck);
811         torture_assert_ntstatus_ok(torture, status, "lock");
812
813         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
814                                        &cc_copy,
815                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
816         torture_assert_ndr_success(torture, ndr_ret,
817                                    "ndr_push_srv_copychunk_copy");
818
819         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
820         /*
821          * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
822          *
823          * Edgar Olougouna @ MS wrote:
824          * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
825          * discrepancy observed between Windows versions, we confirm that the
826          * behavior change is expected.
827          *
828          * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
829          * to move the chunks from the source to the destination.
830          * These ReadFile/WriteFile APIs go through the byte-range lock checks,
831          * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
832          *
833          * Prior to Windows Server 2012, CopyChunk used mapped sections to move
834          * the data. And byte range locks are not enforced on mapped I/O, and
835          * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
836          */
837         torture_assert_ntstatus_equal(torture, status,
838                                       NT_STATUS_FILE_LOCK_CONFLICT,
839                                       "FSCTL_SRV_COPYCHUNK locked");
840
841         /* should get cc response data with the lock conflict status */
842         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
843                                        &cc_rsp,
844                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
845         torture_assert_ndr_success(torture, ndr_ret,
846                                    "ndr_pull_srv_copychunk_rsp");
847         ok = check_copy_chunk_rsp(torture, &cc_rsp,
848                                   0,    /* chunks written */
849                                   0,    /* chunk bytes unsuccessfully written */
850                                   0);   /* total bytes written */
851
852         lck.in.lock_count       = 0x0001;
853         lck.in.lock_sequence    = 0x00000001;
854         lck.in.file.handle      = src_h2;
855         lck.in.locks            = el;
856         el[0].offset            = cc_copy.chunks[0].source_off;
857         el[0].length            = cc_copy.chunks[0].length;
858         el[0].reserved          = 0;
859         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
860         status = smb2_lock(tree, &lck);
861         torture_assert_ntstatus_ok(torture, status, "unlock");
862
863         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
864         torture_assert_ntstatus_ok(torture, status,
865                                    "FSCTL_SRV_COPYCHUNK unlocked");
866
867         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
868                                        &cc_rsp,
869                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
870         torture_assert_ndr_success(torture, ndr_ret,
871                                    "ndr_pull_srv_copychunk_rsp");
872
873         ok = check_copy_chunk_rsp(torture, &cc_rsp,
874                                   1,    /* chunks written */
875                                   0,    /* chunk bytes unsuccessfully written */
876                                   4096); /* total bytes written */
877         if (!ok) {
878                 torture_fail(torture, "bad copy chunk response data");
879         }
880
881         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
882         if (!ok) {
883                 torture_fail(torture, "inconsistent file data");
884         }
885
886         smb2_util_close(tree, src_h2);
887         smb2_util_close(tree, src_h);
888         smb2_util_close(tree, dest_h);
889         talloc_free(tmp_ctx);
890         return true;
891 }
892
893 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
894                                            struct smb2_tree *tree)
895 {
896         struct smb2_handle src_h;
897         struct smb2_handle dest_h;
898         struct smb2_handle dest_h2;
899         NTSTATUS status;
900         union smb_ioctl ioctl;
901         TALLOC_CTX *tmp_ctx = talloc_new(tree);
902         struct srv_copychunk_copy cc_copy;
903         struct srv_copychunk_rsp cc_rsp;
904         enum ndr_err_code ndr_ret;
905         bool ok;
906         struct smb2_lock lck;
907         struct smb2_lock_element el[1];
908
909         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
910                                    1, /* chunks */
911                                    &src_h, 4096, /* src file */
912                                    SEC_RIGHTS_FILE_ALL,
913                                    &dest_h, 4096,       /* dest file */
914                                    SEC_RIGHTS_FILE_ALL,
915                                    &cc_copy,
916                                    &ioctl);
917         if (!ok) {
918                 torture_fail(torture, "setup copy chunk error");
919         }
920
921         cc_copy.chunks[0].source_off = 0;
922         cc_copy.chunks[0].target_off = 0;
923         cc_copy.chunks[0].length = 4096;
924
925         /* open and lock the copychunk dest file */
926         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
927         torture_assert_ntstatus_ok(torture, status, "2nd src open");
928
929         lck.in.lock_count       = 0x0001;
930         lck.in.lock_sequence    = 0x00000000;
931         lck.in.file.handle      = dest_h2;
932         lck.in.locks            = el;
933         el[0].offset            = cc_copy.chunks[0].target_off;
934         el[0].length            = cc_copy.chunks[0].length;
935         el[0].reserved          = 0;
936         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
937
938         status = smb2_lock(tree, &lck);
939         torture_assert_ntstatus_ok(torture, status, "lock");
940
941         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
942                                        &cc_copy,
943                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
944         torture_assert_ndr_success(torture, ndr_ret,
945                                    "ndr_push_srv_copychunk_copy");
946
947         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
948         torture_assert_ntstatus_equal(torture, status,
949                                       NT_STATUS_FILE_LOCK_CONFLICT,
950                                       "FSCTL_SRV_COPYCHUNK locked");
951
952         lck.in.lock_count       = 0x0001;
953         lck.in.lock_sequence    = 0x00000001;
954         lck.in.file.handle      = dest_h2;
955         lck.in.locks            = el;
956         el[0].offset            = cc_copy.chunks[0].target_off;
957         el[0].length            = cc_copy.chunks[0].length;
958         el[0].reserved          = 0;
959         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
960         status = smb2_lock(tree, &lck);
961         torture_assert_ntstatus_ok(torture, status, "unlock");
962
963         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
964         torture_assert_ntstatus_ok(torture, status,
965                                    "FSCTL_SRV_COPYCHUNK unlocked");
966
967         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
968                                        &cc_rsp,
969                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
970         torture_assert_ndr_success(torture, ndr_ret,
971                                    "ndr_pull_srv_copychunk_rsp");
972
973         ok = check_copy_chunk_rsp(torture, &cc_rsp,
974                                   1,    /* chunks written */
975                                   0,    /* chunk bytes unsuccessfully written */
976                                   4096); /* total bytes written */
977         if (!ok) {
978                 torture_fail(torture, "bad copy chunk response data");
979         }
980
981         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
982         if (!ok) {
983                 torture_fail(torture, "inconsistent file data");
984         }
985
986         smb2_util_close(tree, dest_h2);
987         smb2_util_close(tree, src_h);
988         smb2_util_close(tree, dest_h);
989         talloc_free(tmp_ctx);
990         return true;
991 }
992
993 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
994                                           struct smb2_tree *tree)
995 {
996         struct smb2_handle src_h;
997         struct smb2_handle dest_h;
998         NTSTATUS status;
999         union smb_ioctl ioctl;
1000         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1001         struct srv_copychunk_copy cc_copy;
1002         enum ndr_err_code ndr_ret;
1003         bool ok;
1004
1005         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1006                                    1,
1007                                    &src_h, 4096,
1008                                    SEC_RIGHTS_FILE_ALL,
1009                                    &dest_h, 0,
1010                                    SEC_RIGHTS_FILE_ALL,
1011                                    &cc_copy,
1012                                    &ioctl);
1013         if (!ok) {
1014                 torture_fail(torture, "setup copy chunk error");
1015         }
1016
1017         /* overwrite the resume key with a bogus value */
1018         memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
1019
1020         cc_copy.chunks[0].source_off = 0;
1021         cc_copy.chunks[0].target_off = 0;
1022         cc_copy.chunks[0].length = 4096;
1023
1024         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1025                                        &cc_copy,
1026                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1027         torture_assert_ndr_success(torture, ndr_ret,
1028                                    "ndr_push_srv_copychunk_copy");
1029
1030         /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
1031         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1032         torture_assert_ntstatus_equal(torture, status,
1033                                       NT_STATUS_OBJECT_NAME_NOT_FOUND,
1034                                       "FSCTL_SRV_COPYCHUNK");
1035
1036         smb2_util_close(tree, src_h);
1037         smb2_util_close(tree, dest_h);
1038         talloc_free(tmp_ctx);
1039         return true;
1040 }
1041
1042 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
1043                                               struct smb2_tree *tree)
1044 {
1045         struct smb2_handle src_h;
1046         struct smb2_handle dest_h;
1047         NTSTATUS status;
1048         union smb_ioctl ioctl;
1049         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1050         struct srv_copychunk_copy cc_copy;
1051         struct srv_copychunk_rsp cc_rsp;
1052         enum ndr_err_code ndr_ret;
1053         bool ok;
1054
1055         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1056                                    1,
1057                                    &src_h, 8192,
1058                                    SEC_RIGHTS_FILE_ALL,
1059                                    &dest_h, 0,
1060                                    SEC_RIGHTS_FILE_ALL,
1061                                    &cc_copy,
1062                                    &ioctl);
1063         if (!ok) {
1064                 torture_fail(torture, "setup copy chunk error");
1065         }
1066
1067         /* the source is also the destination */
1068         ioctl.smb2.in.file.handle = src_h;
1069
1070         /* non-overlapping */
1071         cc_copy.chunks[0].source_off = 0;
1072         cc_copy.chunks[0].target_off = 4096;
1073         cc_copy.chunks[0].length = 4096;
1074
1075         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1076                                        &cc_copy,
1077                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1078         torture_assert_ndr_success(torture, ndr_ret,
1079                                    "ndr_push_srv_copychunk_copy");
1080
1081         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1082         torture_assert_ntstatus_ok(torture, status,
1083                                    "FSCTL_SRV_COPYCHUNK");
1084
1085         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1086                                        &cc_rsp,
1087                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1088         torture_assert_ndr_success(torture, ndr_ret,
1089                                    "ndr_pull_srv_copychunk_rsp");
1090
1091         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1092                                   1,    /* chunks written */
1093                                   0,    /* chunk bytes unsuccessfully written */
1094                                   4096); /* total bytes written */
1095         if (!ok) {
1096                 torture_fail(torture, "bad copy chunk response data");
1097         }
1098
1099         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1100         if (!ok) {
1101                 torture_fail(torture, "inconsistent file data");
1102         }
1103         ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1104         if (!ok) {
1105                 torture_fail(torture, "inconsistent file data");
1106         }
1107
1108         smb2_util_close(tree, src_h);
1109         smb2_util_close(tree, dest_h);
1110         talloc_free(tmp_ctx);
1111         return true;
1112 }
1113
1114 /*
1115  * Test a single-chunk copychunk request, where the source and target ranges
1116  * overlap, and the SourceKey refers to the same target file. E.g:
1117  *
1118  * Initial State
1119  * -------------
1120  *      File:           src_and_dest
1121  *      Offset:         0123456789
1122  *      Data:           abcdefghij
1123  *
1124  * Request
1125  * -------
1126  *      FSCTL_SRV_COPYCHUNK(src_and_dest)
1127  *      SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1128  *      ChunkCount = 1
1129  *      Chunks[0].SourceOffset = 0
1130  *      Chunks[0].TargetOffset = 4
1131  *      Chunks[0].Length = 6
1132  *
1133  * Resultant State
1134  * ---------------
1135  *      File:           src_and_dest
1136  *      Offset:         0123456789
1137  *      Data:           abcdabcdef
1138  *
1139  * The resultant contents of src_and_dest is dependent on the server's
1140  * copy algorithm. In the above example, the server uses an IO buffer
1141  * large enough to hold the entire six-byte source data before writing
1142  * to TargetOffset. If the server were to use a four-byte IO buffer and
1143  * started reads/writes from the lowest offset, then the two overlapping
1144  * bytes in the above example would be overwritten before being read. The
1145  * resultant file contents would be abcdabcdab.
1146  *
1147  * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1148  * after this offset are written before being read. Windows 2012 on the
1149  * other hand appears to use a buffer large enough to hold its maximum
1150  * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1151  * default (vfs_cc_state.buf).
1152  *
1153  * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1154  * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1155  * to use a different copy algorithm to 2008r2.
1156  */
1157 static bool
1158 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1159                                           struct smb2_tree *tree)
1160 {
1161         struct smb2_handle src_h;
1162         struct smb2_handle dest_h;
1163         NTSTATUS status;
1164         union smb_ioctl ioctl;
1165         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1166         struct srv_copychunk_copy cc_copy;
1167         struct srv_copychunk_rsp cc_rsp;
1168         enum ndr_err_code ndr_ret;
1169         bool ok;
1170
1171         /* exceed the vfs_default copy buffer */
1172         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1173                                    1,
1174                                    &src_h, 2048 * 2,
1175                                    SEC_RIGHTS_FILE_ALL,
1176                                    &dest_h, 0,
1177                                    SEC_RIGHTS_FILE_ALL,
1178                                    &cc_copy,
1179                                    &ioctl);
1180         if (!ok) {
1181                 torture_fail(torture, "setup copy chunk error");
1182         }
1183
1184         /* the source is also the destination */
1185         ioctl.smb2.in.file.handle = src_h;
1186
1187         /* 8 bytes overlap between source and target ranges */
1188         cc_copy.chunks[0].source_off = 0;
1189         cc_copy.chunks[0].target_off = 2048 - 8;
1190         cc_copy.chunks[0].length = 2048;
1191
1192         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1193                                        &cc_copy,
1194                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1195         torture_assert_ndr_success(torture, ndr_ret,
1196                                    "ndr_push_srv_copychunk_copy");
1197
1198         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1199         torture_assert_ntstatus_ok(torture, status,
1200                                    "FSCTL_SRV_COPYCHUNK");
1201
1202         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1203                                        &cc_rsp,
1204                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1205         torture_assert_ndr_success(torture, ndr_ret,
1206                                    "ndr_pull_srv_copychunk_rsp");
1207
1208         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1209                                   1,    /* chunks written */
1210                                   0,    /* chunk bytes unsuccessfully written */
1211                                   2048); /* total bytes written */
1212         if (!ok) {
1213                 torture_fail(torture, "bad copy chunk response data");
1214         }
1215
1216         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1217         if (!ok) {
1218                 torture_fail(torture, "inconsistent file data");
1219         }
1220         ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1221         if (!ok) {
1222                 torture_fail(torture, "inconsistent file data");
1223         }
1224
1225         smb2_util_close(tree, src_h);
1226         smb2_util_close(tree, dest_h);
1227         talloc_free(tmp_ctx);
1228         return true;
1229 }
1230
1231 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1232                                              struct smb2_tree *tree)
1233 {
1234         struct smb2_handle src_h;
1235         struct smb2_handle dest_h;
1236         NTSTATUS status;
1237         union smb_ioctl ioctl;
1238         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1239         struct srv_copychunk_copy cc_copy;
1240         enum ndr_err_code ndr_ret;
1241         bool ok;
1242
1243         /* no read permission on src */
1244         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1245                                    1, /* 1 chunk */
1246                                    &src_h, 4096, /* fill 4096 byte src file */
1247                                    SEC_RIGHTS_FILE_WRITE,
1248                                    &dest_h, 0,  /* 0 byte dest file */
1249                                    SEC_RIGHTS_FILE_ALL,
1250                                    &cc_copy,
1251                                    &ioctl);
1252         if (!ok) {
1253                 torture_fail(torture, "setup copy chunk error");
1254         }
1255
1256         cc_copy.chunks[0].source_off = 0;
1257         cc_copy.chunks[0].target_off = 0;
1258         cc_copy.chunks[0].length = 4096;
1259
1260         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1261                                        &cc_copy,
1262                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1263         torture_assert_ndr_success(torture, ndr_ret,
1264                                    "ndr_push_srv_copychunk_copy");
1265
1266         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1267         torture_assert_ntstatus_equal(torture, status,
1268                                       NT_STATUS_ACCESS_DENIED,
1269                                       "FSCTL_SRV_COPYCHUNK");
1270
1271         smb2_util_close(tree, src_h);
1272         smb2_util_close(tree, dest_h);
1273
1274         /* no write permission on dest */
1275         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1276                                    1, /* 1 chunk */
1277                                    &src_h, 4096, /* fill 4096 byte src file */
1278                                    SEC_RIGHTS_FILE_ALL,
1279                                    &dest_h, 0,  /* 0 byte dest file */
1280                                    (SEC_RIGHTS_FILE_READ
1281                                     | SEC_RIGHTS_FILE_EXECUTE),
1282                                    &cc_copy,
1283                                    &ioctl);
1284         if (!ok) {
1285                 torture_fail(torture, "setup copy chunk error");
1286         }
1287
1288         cc_copy.chunks[0].source_off = 0;
1289         cc_copy.chunks[0].target_off = 0;
1290         cc_copy.chunks[0].length = 4096;
1291
1292         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1293                                        &cc_copy,
1294                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1295         torture_assert_ndr_success(torture, ndr_ret,
1296                                    "ndr_push_srv_copychunk_copy");
1297
1298         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1299         torture_assert_ntstatus_equal(torture, status,
1300                                       NT_STATUS_ACCESS_DENIED,
1301                                       "FSCTL_SRV_COPYCHUNK");
1302
1303         smb2_util_close(tree, src_h);
1304         smb2_util_close(tree, dest_h);
1305
1306         /* no read permission on dest */
1307         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1308                                    1, /* 1 chunk */
1309                                    &src_h, 4096, /* fill 4096 byte src file */
1310                                    SEC_RIGHTS_FILE_ALL,
1311                                    &dest_h, 0,  /* 0 byte dest file */
1312                                    (SEC_RIGHTS_FILE_WRITE
1313                                     | SEC_RIGHTS_FILE_EXECUTE),
1314                                    &cc_copy,
1315                                    &ioctl);
1316         if (!ok) {
1317                 torture_fail(torture, "setup copy chunk error");
1318         }
1319
1320         cc_copy.chunks[0].source_off = 0;
1321         cc_copy.chunks[0].target_off = 0;
1322         cc_copy.chunks[0].length = 4096;
1323
1324         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1325                                        &cc_copy,
1326                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1327         torture_assert_ndr_success(torture, ndr_ret,
1328                                    "ndr_push_srv_copychunk_copy");
1329
1330         /*
1331          * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1332          * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1333          */
1334         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1335         torture_assert_ntstatus_equal(torture, status,
1336                                       NT_STATUS_ACCESS_DENIED,
1337                                       "FSCTL_SRV_COPYCHUNK");
1338
1339         smb2_util_close(tree, src_h);
1340         smb2_util_close(tree, dest_h);
1341         talloc_free(tmp_ctx);
1342
1343         return true;
1344 }
1345
1346 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1347                                                struct smb2_tree *tree)
1348 {
1349         struct smb2_handle src_h;
1350         struct smb2_handle dest_h;
1351         NTSTATUS status;
1352         union smb_ioctl ioctl;
1353         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1354         struct srv_copychunk_copy cc_copy;
1355         enum ndr_err_code ndr_ret;
1356         bool ok;
1357
1358         /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1359         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1360                                    1, /* 1 chunk */
1361                                    &src_h, 4096, /* fill 4096 byte src file */
1362                                    SEC_RIGHTS_FILE_ALL,
1363                                    &dest_h, 0,  /* 0 byte dest file */
1364                                    (SEC_RIGHTS_FILE_WRITE
1365                                     | SEC_RIGHTS_FILE_EXECUTE),
1366                                    &cc_copy,
1367                                    &ioctl);
1368         if (!ok) {
1369                 torture_fail(torture, "setup copy chunk error");
1370         }
1371
1372         ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1373         cc_copy.chunks[0].source_off = 0;
1374         cc_copy.chunks[0].target_off = 0;
1375         cc_copy.chunks[0].length = 4096;
1376
1377         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1378                                        &cc_copy,
1379                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1380         torture_assert_ndr_success(torture, ndr_ret,
1381                                    "ndr_push_srv_copychunk_copy");
1382
1383         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1384         torture_assert_ntstatus_ok(torture, status,
1385                                    "FSCTL_SRV_COPYCHUNK_WRITE");
1386
1387         smb2_util_close(tree, src_h);
1388         smb2_util_close(tree, dest_h);
1389         talloc_free(tmp_ctx);
1390
1391         return true;
1392 }
1393
1394 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1395                                              struct smb2_tree *tree)
1396 {
1397         struct smb2_handle src_h;
1398         struct smb2_handle dest_h;
1399         NTSTATUS status;
1400         union smb_ioctl ioctl;
1401         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1402         struct srv_copychunk_copy cc_copy;
1403         struct srv_copychunk_rsp cc_rsp;
1404         enum ndr_err_code ndr_ret;
1405         bool ok;
1406
1407         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1408                                    1, /* 1 chunk */
1409                                    &src_h, 4096, /* fill 4096 byte src file */
1410                                    SEC_RIGHTS_FILE_ALL,
1411                                    &dest_h, 0,  /* 0 byte dest file */
1412                                    SEC_RIGHTS_FILE_ALL,
1413                                    &cc_copy,
1414                                    &ioctl);
1415         if (!ok) {
1416                 torture_fail(torture, "setup copy chunk error");
1417         }
1418
1419         /* Request copy where off + length exceeds size of src */
1420         cc_copy.chunks[0].source_off = 1024;
1421         cc_copy.chunks[0].target_off = 0;
1422         cc_copy.chunks[0].length = 4096;
1423
1424         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1425                                        &cc_copy,
1426                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1427         torture_assert_ndr_success(torture, ndr_ret,
1428                                    "ndr_push_srv_copychunk_copy");
1429
1430         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1431         torture_assert_ntstatus_equal(torture, status,
1432                                       NT_STATUS_INVALID_VIEW_SIZE,
1433                                       "FSCTL_SRV_COPYCHUNK oversize");
1434
1435         /* Request copy where length exceeds size of src */
1436         cc_copy.chunks[0].source_off = 1024;
1437         cc_copy.chunks[0].target_off = 0;
1438         cc_copy.chunks[0].length = 3072;
1439
1440         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1441                                        &cc_copy,
1442                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1443         torture_assert_ndr_success(torture, ndr_ret,
1444                                    "ndr_push_srv_copychunk_copy");
1445
1446         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1447         torture_assert_ntstatus_ok(torture, status,
1448                                    "FSCTL_SRV_COPYCHUNK just right");
1449
1450         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1451                                        &cc_rsp,
1452                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1453         torture_assert_ndr_success(torture, ndr_ret,
1454                                    "ndr_pull_srv_copychunk_rsp");
1455
1456         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1457                                   1,    /* chunks written */
1458                                   0,    /* chunk bytes unsuccessfully written */
1459                                   3072); /* total bytes written */
1460         if (!ok) {
1461                 torture_fail(torture, "bad copy chunk response data");
1462         }
1463
1464         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1465         if (!ok) {
1466                 torture_fail(torture, "inconsistent file data");
1467         }
1468
1469         smb2_util_close(tree, src_h);
1470         smb2_util_close(tree, dest_h);
1471         talloc_free(tmp_ctx);
1472         return true;
1473 }
1474
1475 static bool
1476 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1477                                        struct smb2_tree *tree)
1478 {
1479         struct smb2_handle src_h;
1480         struct smb2_handle dest_h;
1481         NTSTATUS status;
1482         union smb_ioctl ioctl;
1483         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1484         struct srv_copychunk_copy cc_copy;
1485         struct srv_copychunk_rsp cc_rsp;
1486         enum ndr_err_code ndr_ret;
1487         bool ok;
1488
1489         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1490                                    2, /* 2 chunks */
1491                                    &src_h, 8192, /* fill 8192 byte src file */
1492                                    SEC_RIGHTS_FILE_ALL,
1493                                    &dest_h, 0,  /* 0 byte dest file */
1494                                    SEC_RIGHTS_FILE_ALL,
1495                                    &cc_copy,
1496                                    &ioctl);
1497         if (!ok) {
1498                 torture_fail(torture, "setup copy chunk error");
1499         }
1500
1501         /* Request copy where off + length exceeds size of src */
1502         cc_copy.chunks[0].source_off = 0;
1503         cc_copy.chunks[0].target_off = 0;
1504         cc_copy.chunks[0].length = 4096;
1505
1506         cc_copy.chunks[1].source_off = 4096;
1507         cc_copy.chunks[1].target_off = 4096;
1508         cc_copy.chunks[1].length = 8192;
1509
1510         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1511                                        &cc_copy,
1512                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1513         torture_assert_ndr_success(torture, ndr_ret,
1514                                    "ndr_push_srv_copychunk_copy");
1515
1516         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1517         torture_assert_ntstatus_equal(torture, status,
1518                                       NT_STATUS_INVALID_VIEW_SIZE,
1519                                       "FSCTL_SRV_COPYCHUNK oversize");
1520         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1521                                        &cc_rsp,
1522                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1523         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1524
1525         /* first chunk should still be written */
1526         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1527                                   1,    /* chunks written */
1528                                   0,    /* chunk bytes unsuccessfully written */
1529                                   4096); /* total bytes written */
1530         if (!ok) {
1531                 torture_fail(torture, "bad copy chunk response data");
1532         }
1533         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1534         if (!ok) {
1535                 torture_fail(torture, "inconsistent file data");
1536         }
1537
1538         smb2_util_close(tree, src_h);
1539         smb2_util_close(tree, dest_h);
1540         talloc_free(tmp_ctx);
1541         return true;
1542 }
1543
1544 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1545                                               struct smb2_tree *tree)
1546 {
1547         struct smb2_handle src_h;
1548         struct smb2_handle dest_h;
1549         NTSTATUS status;
1550         union smb_ioctl ioctl;
1551         struct smb2_read r;
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         int i;
1558
1559         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1560                                    1, /* 1 chunk */
1561                                    &src_h, 4096, /* fill 4096 byte src file */
1562                                    SEC_RIGHTS_FILE_ALL,
1563                                    &dest_h, 0,  /* 0 byte dest file */
1564                                    SEC_RIGHTS_FILE_ALL,
1565                                    &cc_copy,
1566                                    &ioctl);
1567         if (!ok) {
1568                 torture_fail(torture, "setup copy chunk error");
1569         }
1570
1571         /* copy all src file data (via a single chunk desc) */
1572         cc_copy.chunks[0].source_off = 0;
1573         cc_copy.chunks[0].target_off = 4096;
1574         cc_copy.chunks[0].length = 4096;
1575
1576         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1577                                        &cc_copy,
1578                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1579         torture_assert_ndr_success(torture, ndr_ret,
1580                                    "ndr_push_srv_copychunk_copy");
1581
1582         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1583         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1584
1585         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1586                                        &cc_rsp,
1587                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1588         torture_assert_ndr_success(torture, ndr_ret,
1589                                    "ndr_pull_srv_copychunk_rsp");
1590
1591         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1592                                   1,    /* chunks written */
1593                                   0,    /* chunk bytes unsuccessfully written */
1594                                   4096); /* total bytes written */
1595         if (!ok) {
1596                 torture_fail(torture, "bad copy chunk response data");
1597         }
1598
1599         /* check for zeros in first 4k */
1600         ZERO_STRUCT(r);
1601         r.in.file.handle = dest_h;
1602         r.in.length      = 4096;
1603         r.in.offset      = 0;
1604         status = smb2_read(tree, tmp_ctx, &r);
1605         torture_assert_ntstatus_ok(torture, status, "read");
1606
1607         torture_assert_u64_equal(torture, r.out.data.length, 4096,
1608                                  "read data len mismatch");
1609
1610         for (i = 0; i < 4096; i++) {
1611                 torture_assert(torture, (r.out.data.data[i] == 0),
1612                                "sparse did not pass class");
1613         }
1614
1615         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1616         if (!ok) {
1617                 torture_fail(torture, "inconsistent file data");
1618         }
1619
1620         smb2_util_close(tree, src_h);
1621         smb2_util_close(tree, dest_h);
1622         talloc_free(tmp_ctx);
1623         return true;
1624 }
1625
1626 /*
1627  * set the ioctl MaxOutputResponse size to less than
1628  * sizeof(struct srv_copychunk_rsp)
1629  */
1630 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1631                                                 struct smb2_tree *tree)
1632 {
1633         struct smb2_handle src_h;
1634         struct smb2_handle dest_h;
1635         NTSTATUS status;
1636         union smb_ioctl ioctl;
1637         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1638         struct srv_copychunk_copy cc_copy;
1639         enum ndr_err_code ndr_ret;
1640         bool ok;
1641
1642         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1643                                    1, /* 1 chunk */
1644                                    &src_h, 4096, /* fill 4096 byte src file */
1645                                    SEC_RIGHTS_FILE_ALL,
1646                                    &dest_h, 0,  /* 0 byte dest file */
1647                                    SEC_RIGHTS_FILE_ALL,
1648                                    &cc_copy,
1649                                    &ioctl);
1650         if (!ok) {
1651                 torture_fail(torture, "setup copy chunk error");
1652         }
1653
1654         cc_copy.chunks[0].source_off = 0;
1655         cc_copy.chunks[0].target_off = 0;
1656         cc_copy.chunks[0].length = 4096;
1657         /* req is valid, but use undersize max_response_size */
1658         ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1659
1660         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1661                                        &cc_copy,
1662                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1663         torture_assert_ndr_success(torture, ndr_ret,
1664                                    "ndr_push_srv_copychunk_copy");
1665
1666         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1667         torture_assert_ntstatus_equal(torture, status,
1668                                       NT_STATUS_INVALID_PARAMETER,
1669                                       "FSCTL_SRV_COPYCHUNK");
1670
1671         smb2_util_close(tree, src_h);
1672         smb2_util_close(tree, dest_h);
1673         talloc_free(tmp_ctx);
1674         return true;
1675 }
1676
1677 static bool test_ioctl_copy_chunk_zero_length(struct torture_context *torture,
1678                                               struct smb2_tree *tree)
1679 {
1680         struct smb2_handle src_h;
1681         struct smb2_handle dest_h;
1682         NTSTATUS status;
1683         union smb_ioctl ioctl;
1684         union smb_fileinfo q;
1685         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1686         struct srv_copychunk_copy cc_copy;
1687         struct srv_copychunk_rsp cc_rsp;
1688         enum ndr_err_code ndr_ret;
1689         bool ok;
1690
1691         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1692                                    1, /* 1 chunk */
1693                                    &src_h, 4096, /* fill 4096 byte src file */
1694                                    SEC_RIGHTS_FILE_ALL,
1695                                    &dest_h, 0,  /* 0 byte dest file */
1696                                    SEC_RIGHTS_FILE_ALL,
1697                                    &cc_copy,
1698                                    &ioctl);
1699         if (!ok) {
1700                 torture_fail(torture, "setup copy chunk error");
1701         }
1702
1703         /* zero length server-side copy (via a single chunk desc) */
1704         cc_copy.chunks[0].source_off = 0;
1705         cc_copy.chunks[0].target_off = 0;
1706         cc_copy.chunks[0].length = 0;
1707
1708         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1709                                        &cc_copy,
1710                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1711         torture_assert_ndr_success(torture, ndr_ret,
1712                                    "ndr_push_srv_copychunk_copy");
1713
1714         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1715         torture_assert_ntstatus_equal(torture, status,
1716                                       NT_STATUS_INVALID_PARAMETER,
1717                                       "bad zero-length chunk response");
1718
1719         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1720                                        &cc_rsp,
1721                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1722         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1723
1724         ZERO_STRUCT(q);
1725         q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1726         q.all_info2.in.file.handle = dest_h;
1727         status = smb2_getinfo_file(tree, torture, &q);
1728         torture_assert_ntstatus_ok(torture, status, "getinfo");
1729
1730         torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1731                                  "size after zero len clone");
1732
1733         smb2_util_close(tree, src_h);
1734         smb2_util_close(tree, dest_h);
1735         talloc_free(tmp_ctx);
1736         return true;
1737 }
1738
1739 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1740                                                  struct smb2_tree *tree,
1741                                                  TALLOC_CTX *mem_ctx,
1742                                                  struct smb2_handle *fh,
1743                                                  bool *compress_support)
1744 {
1745         NTSTATUS status;
1746         union smb_fsinfo info;
1747
1748         ZERO_STRUCT(info);
1749         info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1750         info.generic.handle = *fh;
1751         status = smb2_getinfo_fs(tree, tree, &info);
1752         if (!NT_STATUS_IS_OK(status)) {
1753                 return status;
1754         }
1755
1756         if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1757                 *compress_support = true;
1758         } else {
1759                 *compress_support = false;
1760         }
1761         return NT_STATUS_OK;
1762 }
1763
1764 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1765                                         TALLOC_CTX *mem_ctx,
1766                                         struct smb2_tree *tree,
1767                                         struct smb2_handle fh,
1768                                         uint16_t *_compression_fmt)
1769 {
1770         union smb_ioctl ioctl;
1771         struct compression_state cmpr_state;
1772         enum ndr_err_code ndr_ret;
1773         NTSTATUS status;
1774
1775         ZERO_STRUCT(ioctl);
1776         ioctl.smb2.level = RAW_IOCTL_SMB2;
1777         ioctl.smb2.in.file.handle = fh;
1778         ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1779         ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1780         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1781
1782         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1783         if (!NT_STATUS_IS_OK(status)) {
1784                 return status;
1785         }
1786
1787         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1788                                        &cmpr_state,
1789                         (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1790
1791         if (ndr_ret != NDR_ERR_SUCCESS) {
1792                 return NT_STATUS_INTERNAL_ERROR;
1793         }
1794
1795         *_compression_fmt = cmpr_state.format;
1796         return NT_STATUS_OK;
1797 }
1798
1799 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1800                                         TALLOC_CTX *mem_ctx,
1801                                         struct smb2_tree *tree,
1802                                         struct smb2_handle fh,
1803                                         uint16_t compression_fmt)
1804 {
1805         union smb_ioctl ioctl;
1806         struct compression_state cmpr_state;
1807         enum ndr_err_code ndr_ret;
1808         NTSTATUS status;
1809
1810         ZERO_STRUCT(ioctl);
1811         ioctl.smb2.level = RAW_IOCTL_SMB2;
1812         ioctl.smb2.in.file.handle = fh;
1813         ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1814         ioctl.smb2.in.max_response_size = 0;
1815         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1816
1817         cmpr_state.format = compression_fmt;
1818         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1819                                        &cmpr_state,
1820                         (ndr_push_flags_fn_t)ndr_push_compression_state);
1821         if (ndr_ret != NDR_ERR_SUCCESS) {
1822                 return NT_STATUS_INTERNAL_ERROR;
1823         }
1824
1825         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1826         return status;
1827 }
1828
1829 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1830                                             struct smb2_tree *tree)
1831 {
1832         struct smb2_handle fh;
1833         NTSTATUS status;
1834         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1835         bool ok;
1836         uint16_t compression_fmt;
1837
1838         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1839                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1840                                     FILE_ATTRIBUTE_NORMAL);
1841         torture_assert(torture, ok, "setup compression file");
1842
1843         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1844                                                   &ok);
1845         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1846         if (!ok) {
1847                 smb2_util_close(tree, fh);
1848                 torture_skip(torture, "FS compression not supported\n");
1849         }
1850
1851         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1852                                          &compression_fmt);
1853         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1854
1855         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1856                        "initial compression state not NONE");
1857
1858         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1859                                          COMPRESSION_FORMAT_DEFAULT);
1860         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1861
1862         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1863                                          &compression_fmt);
1864         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1865
1866         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1867                        "invalid compression state after set");
1868
1869         smb2_util_close(tree, fh);
1870         talloc_free(tmp_ctx);
1871         return true;
1872 }
1873
1874 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1875                                             struct smb2_tree *tree)
1876 {
1877         struct smb2_handle dirh;
1878         struct smb2_handle fh;
1879         NTSTATUS status;
1880         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1881         uint16_t compression_fmt;
1882         bool ok;
1883         char path_buf[PATH_MAX];
1884
1885         smb2_deltree(tree, DNAME);
1886         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1887                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1888                                     FILE_ATTRIBUTE_DIRECTORY);
1889         torture_assert(torture, ok, "setup compression directory");
1890
1891         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1892                                                   &ok);
1893         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1894         if (!ok) {
1895                 smb2_util_close(tree, dirh);
1896                 smb2_deltree(tree, DNAME);
1897                 torture_skip(torture, "FS compression not supported\n");
1898         }
1899
1900         /* set compression on parent dir, then check for inheritance */
1901         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1902                                          COMPRESSION_FORMAT_LZNT1);
1903         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1904
1905         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1906                                          &compression_fmt);
1907         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1908
1909         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1910                        "invalid compression state after set");
1911
1912         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1913         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1914                                     path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1915                                     FILE_ATTRIBUTE_NORMAL);
1916         torture_assert(torture, ok, "setup compression file");
1917
1918         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1919                                          &compression_fmt);
1920         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1921
1922         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1923                        "compression attr not inherited by new file");
1924
1925         /* check compressed data is consistent */
1926         ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1927
1928         /* disable dir compression attr, file should remain compressed */
1929         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1930                                          COMPRESSION_FORMAT_NONE);
1931         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1932
1933         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1934                                          &compression_fmt);
1935         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1936
1937         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1938                        "file compression attr removed after dir change");
1939         smb2_util_close(tree, fh);
1940
1941         /* new files should no longer inherit compression attr */
1942         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1943         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1944                                     path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1945                                     FILE_ATTRIBUTE_NORMAL);
1946         torture_assert(torture, ok, "setup file");
1947
1948         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1949                                          &compression_fmt);
1950         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1951
1952         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1953                        "compression attr present on new file");
1954
1955         smb2_util_close(tree, fh);
1956         smb2_util_close(tree, dirh);
1957         smb2_deltree(tree, DNAME);
1958         talloc_free(tmp_ctx);
1959         return true;
1960 }
1961
1962 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1963                                                struct smb2_tree *tree)
1964 {
1965         struct smb2_handle fh;
1966         NTSTATUS status;
1967         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1968         bool ok;
1969         uint16_t compression_fmt;
1970
1971         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1972                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1973                                     FILE_ATTRIBUTE_NORMAL);
1974         torture_assert(torture, ok, "setup compression file");
1975
1976         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1977                                                   &ok);
1978         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1979         if (!ok) {
1980                 smb2_util_close(tree, fh);
1981                 torture_skip(torture, "FS compression not supported\n");
1982         }
1983
1984         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1985                                          0x0042); /* bogus */
1986         torture_assert_ntstatus_equal(torture, status,
1987                                       NT_STATUS_INVALID_PARAMETER,
1988                                       "invalid FSCTL_SET_COMPRESSION");
1989
1990         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1991                                          &compression_fmt);
1992         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1993
1994         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1995                        "initial compression state not NONE");
1996
1997         smb2_util_close(tree, fh);
1998         talloc_free(tmp_ctx);
1999         return true;
2000 }
2001
2002 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
2003                                             struct smb2_tree *tree)
2004 {
2005         struct smb2_handle fh;
2006         NTSTATUS status;
2007         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2008         bool ok;
2009         union smb_ioctl ioctl;
2010
2011         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2012                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2013                                     FILE_ATTRIBUTE_NORMAL);
2014         torture_assert(torture, ok, "setup compression file");
2015
2016         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2017                                                   &ok);
2018         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2019         if (!ok) {
2020                 smb2_util_close(tree, fh);
2021                 torture_skip(torture, "FS compression not supported\n");
2022         }
2023
2024         ZERO_STRUCT(ioctl);
2025         ioctl.smb2.level = RAW_IOCTL_SMB2;
2026         ioctl.smb2.in.file.handle = fh;
2027         ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
2028         ioctl.smb2.in.max_response_size = 0;    /* no room for rsp data */
2029         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2030
2031         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2032         if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
2033          && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2034                 /* neither Server 2k12 nor 2k8r2 response status */
2035                 torture_assert(torture, true,
2036                                "invalid FSCTL_SET_COMPRESSION");
2037         }
2038
2039         smb2_util_close(tree, fh);
2040         talloc_free(tmp_ctx);
2041         return true;
2042 }
2043
2044 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
2045                                                 struct smb2_tree *tree)
2046 {
2047         struct smb2_handle fh;
2048         union smb_fileinfo io;
2049         NTSTATUS status;
2050         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2051         bool ok;
2052
2053         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2054                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2055                                     FILE_ATTRIBUTE_NORMAL);
2056         torture_assert(torture, ok, "setup compression file");
2057
2058         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2059                                                   &ok);
2060         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2061         if (!ok) {
2062                 smb2_util_close(tree, fh);
2063                 torture_skip(torture, "FS compression not supported\n");
2064         }
2065
2066         ZERO_STRUCT(io);
2067         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2068         io.generic.in.file.handle = fh;
2069         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2070         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2071
2072         torture_assert(torture,
2073                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2074                        "compression attr before set");
2075
2076         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2077                                          COMPRESSION_FORMAT_DEFAULT);
2078         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2079
2080         ZERO_STRUCT(io);
2081         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2082         io.generic.in.file.handle = fh;
2083         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2084         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2085
2086         torture_assert(torture,
2087                        (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2088                        "no compression attr after set");
2089
2090         smb2_util_close(tree, fh);
2091         talloc_free(tmp_ctx);
2092         return true;
2093 }
2094
2095 /*
2096  * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2097  * attribute.
2098  */
2099 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2100                                                  struct smb2_tree *tree)
2101 {
2102         struct smb2_handle fh2;
2103         union smb_fileinfo io;
2104         NTSTATUS status;
2105         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2106         uint16_t compression_fmt;
2107         bool ok;
2108
2109         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2110                                     FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2111                         (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2112         torture_assert(torture, ok, "setup compression file");
2113
2114         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2115                                                   &ok);
2116         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2117         if (!ok) {
2118                 smb2_util_close(tree, fh2);
2119                 torture_skip(torture, "FS compression not supported\n");
2120         }
2121
2122         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2123                                          &compression_fmt);
2124         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2125
2126         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2127                        "initial compression state not NONE");
2128
2129         ZERO_STRUCT(io);
2130         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2131         io.generic.in.file.handle = fh2;
2132         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2133         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2134
2135         torture_assert(torture,
2136                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2137                        "incorrect compression attr");
2138
2139         smb2_util_close(tree, fh2);
2140         talloc_free(tmp_ctx);
2141         return true;
2142 }
2143
2144 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2145                                                 struct smb2_tree *tree)
2146 {
2147         struct smb2_handle fh;
2148         struct smb2_handle dirh;
2149         char path_buf[PATH_MAX];
2150         NTSTATUS status;
2151         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2152         bool ok;
2153         uint16_t compression_fmt;
2154
2155         struct smb2_create io;
2156
2157         smb2_deltree(tree, DNAME);
2158         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2159                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2160                                     FILE_ATTRIBUTE_DIRECTORY);
2161         torture_assert(torture, ok, "setup compression directory");
2162
2163         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2164                                                   &ok);
2165         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2166         if (!ok) {
2167                 smb2_util_close(tree, dirh);
2168                 smb2_deltree(tree, DNAME);
2169                 torture_skip(torture, "FS compression not supported\n");
2170         }
2171
2172         /* set compression on parent dir, then check for inheritance */
2173         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2174                                          COMPRESSION_FORMAT_LZNT1);
2175         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2176
2177         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2178                                          &compression_fmt);
2179         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2180
2181         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2182                        "invalid compression state after set");
2183         smb2_util_close(tree, dirh);
2184
2185         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2186         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2187                                     path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2188                                     FILE_ATTRIBUTE_NORMAL);
2189         torture_assert(torture, ok, "setup compression file");
2190
2191         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2192                                          &compression_fmt);
2193         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2194
2195         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2196                        "compression attr not inherited by new file");
2197         smb2_util_close(tree, fh);
2198
2199         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2200
2201         /* NO_COMPRESSION option should block inheritance */
2202         ZERO_STRUCT(io);
2203         io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2204         io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2205         io.in.create_disposition = NTCREATEX_DISP_CREATE;
2206         io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2207         io.in.share_access =
2208                 NTCREATEX_SHARE_ACCESS_DELETE|
2209                 NTCREATEX_SHARE_ACCESS_READ|
2210                 NTCREATEX_SHARE_ACCESS_WRITE;
2211         io.in.fname = path_buf;
2212
2213         status = smb2_create(tree, tmp_ctx, &io);
2214         torture_assert_ntstatus_ok(torture, status, "file create");
2215
2216         fh = io.out.file.handle;
2217
2218         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2219                                          &compression_fmt);
2220         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2221
2222         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2223                        "compression attr inherited by NO_COMPRESSION file");
2224         smb2_util_close(tree, fh);
2225
2226
2227         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2228         ZERO_STRUCT(io);
2229         io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2230         io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2231         io.in.create_disposition = NTCREATEX_DISP_CREATE;
2232         io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2233                                 | NTCREATEX_OPTIONS_DIRECTORY);
2234         io.in.share_access =
2235                 NTCREATEX_SHARE_ACCESS_DELETE|
2236                 NTCREATEX_SHARE_ACCESS_READ|
2237                 NTCREATEX_SHARE_ACCESS_WRITE;
2238         io.in.fname = path_buf;
2239
2240         status = smb2_create(tree, tmp_ctx, &io);
2241         torture_assert_ntstatus_ok(torture, status, "dir create");
2242
2243         dirh = io.out.file.handle;
2244
2245         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2246                                          &compression_fmt);
2247         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2248
2249         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2250                        "compression attr inherited by NO_COMPRESSION dir");
2251         smb2_util_close(tree, dirh);
2252         smb2_deltree(tree, DNAME);
2253
2254         talloc_free(tmp_ctx);
2255         return true;
2256 }
2257
2258 /* attempting to set compression via SetInfo should not stick */
2259 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2260                                               struct smb2_tree *tree)
2261 {
2262         struct smb2_handle fh;
2263         struct smb2_handle dirh;
2264         union smb_fileinfo io;
2265         union smb_setfileinfo set_io;
2266         uint16_t compression_fmt;
2267         NTSTATUS status;
2268         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2269         bool ok;
2270
2271         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2272                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2273                                     FILE_ATTRIBUTE_NORMAL);
2274         torture_assert(torture, ok, "setup compression file");
2275
2276         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2277                                                   &ok);
2278         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2279         if (!ok) {
2280                 smb2_util_close(tree, fh);
2281                 torture_skip(torture, "FS compression not supported\n");
2282         }
2283
2284         ZERO_STRUCT(io);
2285         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2286         io.generic.in.file.handle = fh;
2287         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2288         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2289
2290         torture_assert(torture,
2291                 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2292                        "compression attr before set");
2293
2294         ZERO_STRUCT(set_io);
2295         set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2296         set_io.basic_info.in.file.handle = fh;
2297         set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2298         set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2299         set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2300         set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2301         set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2302                                                 | FILE_ATTRIBUTE_COMPRESSED);
2303         status = smb2_setinfo_file(tree, &set_io);
2304         torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2305
2306         ZERO_STRUCT(io);
2307         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2308         io.generic.in.file.handle = fh;
2309         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2310         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2311
2312         torture_assert(torture,
2313                 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2314                 "compression attr after set");
2315
2316         smb2_util_close(tree, fh);
2317         smb2_deltree(tree, DNAME);
2318         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2319                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2320                                     FILE_ATTRIBUTE_DIRECTORY);
2321         torture_assert(torture, ok, "setup compression directory");
2322
2323         ZERO_STRUCT(io);
2324         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2325         io.generic.in.file.handle = dirh;
2326         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2327         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2328
2329         torture_assert(torture,
2330                 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2331                        "compression attr before set");
2332
2333         ZERO_STRUCT(set_io);
2334         set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2335         set_io.basic_info.in.file.handle = dirh;
2336         set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2337         set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2338         set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2339         set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2340         set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2341                                                 | FILE_ATTRIBUTE_COMPRESSED);
2342         status = smb2_setinfo_file(tree, &set_io);
2343         torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2344
2345         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2346                                          &compression_fmt);
2347         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2348
2349         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2350                        "dir compression set after SetInfo");
2351
2352         smb2_util_close(tree, dirh);
2353         talloc_free(tmp_ctx);
2354         return true;
2355 }
2356
2357 static bool test_ioctl_compress_perms(struct torture_context *torture,
2358                                       struct smb2_tree *tree)
2359 {
2360         struct smb2_handle fh;
2361         uint16_t compression_fmt;
2362         union smb_fileinfo io;
2363         NTSTATUS status;
2364         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2365         bool ok;
2366
2367         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2368                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2369                                     FILE_ATTRIBUTE_NORMAL);
2370         torture_assert(torture, ok, "setup compression file");
2371
2372         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2373                                                   &ok);
2374         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2375         smb2_util_close(tree, fh);
2376         if (!ok) {
2377                 torture_skip(torture, "FS compression not supported\n");
2378         }
2379
2380         /* attempt get compression without READ_ATTR permission */
2381         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2382                                     FNAME, &fh, 0,
2383                         (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2384                                                         | SEC_STD_READ_CONTROL
2385                                                         | SEC_FILE_READ_EA)),
2386                                     FILE_ATTRIBUTE_NORMAL);
2387         torture_assert(torture, ok, "setup compression file");
2388
2389         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2390                                          &compression_fmt);
2391         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2392         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2393                        "compression set after create");
2394         smb2_util_close(tree, fh);
2395
2396         /* set compression without WRITE_ATTR permission should succeed */
2397         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2398                                     FNAME, &fh, 0,
2399                         (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2400                                                         | SEC_STD_WRITE_DAC
2401                                                         | SEC_FILE_WRITE_EA)),
2402                                     FILE_ATTRIBUTE_NORMAL);
2403         torture_assert(torture, ok, "setup compression file");
2404
2405         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2406                                          COMPRESSION_FORMAT_DEFAULT);
2407         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2408         smb2_util_close(tree, fh);
2409
2410         ok = test_setup_open(torture, tree, tmp_ctx,
2411                                     FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2412                                     FILE_ATTRIBUTE_NORMAL);
2413         torture_assert(torture, ok, "setup compression file");
2414         ZERO_STRUCT(io);
2415         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2416         io.generic.in.file.handle = fh;
2417         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2418         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2419
2420         torture_assert(torture,
2421                        (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2422                        "incorrect compression attr");
2423         smb2_util_close(tree, fh);
2424
2425         /* attempt get compression without READ_DATA permission */
2426         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2427                                     FNAME, &fh, 0,
2428                         (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2429                                     FILE_ATTRIBUTE_NORMAL);
2430         torture_assert(torture, ok, "setup compression file");
2431
2432         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2433                                          &compression_fmt);
2434         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2435         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2436                        "compression enabled after set");
2437         smb2_util_close(tree, fh);
2438
2439         /* attempt get compression with only SYNCHRONIZE permission */
2440         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2441                                     FNAME, &fh, 0,
2442                                     SEC_STD_SYNCHRONIZE,
2443                                     FILE_ATTRIBUTE_NORMAL);
2444         torture_assert(torture, ok, "setup compression file");
2445
2446         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2447                                          &compression_fmt);
2448         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2449         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2450                        "compression not enabled after set");
2451         smb2_util_close(tree, fh);
2452
2453         /* attempt to set compression without WRITE_DATA permission */
2454         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2455                                     FNAME, &fh, 0,
2456                         (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2457                                     FILE_ATTRIBUTE_NORMAL);
2458         torture_assert(torture, ok, "setup compression file");
2459
2460         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2461                                          COMPRESSION_FORMAT_DEFAULT);
2462         torture_assert_ntstatus_equal(torture, status,
2463                                       NT_STATUS_ACCESS_DENIED,
2464                                       "FSCTL_SET_COMPRESSION permission");
2465         smb2_util_close(tree, fh);
2466
2467         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2468                                     FNAME, &fh, 0,
2469                         (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2470                                     FILE_ATTRIBUTE_NORMAL);
2471         torture_assert(torture, ok, "setup compression file");
2472
2473         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2474                                          COMPRESSION_FORMAT_NONE);
2475         torture_assert_ntstatus_equal(torture, status,
2476                                       NT_STATUS_ACCESS_DENIED,
2477                                       "FSCTL_SET_COMPRESSION permission");
2478         smb2_util_close(tree, fh);
2479
2480         talloc_free(tmp_ctx);
2481         return true;
2482 }
2483
2484 /*
2485    basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2486 */
2487 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2488                                       struct smb2_tree *tree)
2489 {
2490         union smb_ioctl ioctl;
2491         struct smb2_handle fh;
2492         NTSTATUS status;
2493         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2494         struct fsctl_net_iface_info net_iface;
2495         enum ndr_err_code ndr_ret;
2496         uint32_t caps;
2497
2498         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2499         if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2500                 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2501         }
2502
2503         ZERO_STRUCT(ioctl);
2504         ioctl.smb2.level = RAW_IOCTL_SMB2;
2505         fh.data[0] = UINT64_MAX;
2506         fh.data[1] = UINT64_MAX;
2507         ioctl.smb2.in.file.handle = fh;
2508         ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2509         ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2510         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2511
2512         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2513         torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2514
2515         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2516                         (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2517         torture_assert_ndr_success(torture, ndr_ret,
2518                                    "ndr_pull_fsctl_net_iface_info");
2519
2520         ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2521                         "Network Interface Info", &net_iface);
2522
2523         talloc_free(tmp_ctx);
2524         return true;
2525 }
2526
2527 static NTSTATUS test_ioctl_sparse_fs_supported(struct torture_context *torture,
2528                                                struct smb2_tree *tree,
2529                                                TALLOC_CTX *mem_ctx,
2530                                                struct smb2_handle *fh,
2531                                                bool *sparse_support)
2532 {
2533         NTSTATUS status;
2534         union smb_fsinfo info;
2535
2536         ZERO_STRUCT(info);
2537         info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2538         info.generic.handle = *fh;
2539         status = smb2_getinfo_fs(tree, tree, &info);
2540         if (!NT_STATUS_IS_OK(status)) {
2541                 return status;
2542         }
2543
2544         if (info.attribute_info.out.fs_attr & FILE_SUPPORTS_SPARSE_FILES) {
2545                 *sparse_support = true;
2546         } else {
2547                 *sparse_support = false;
2548         }
2549         return NT_STATUS_OK;
2550 }
2551
2552 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2553                                       TALLOC_CTX *mem_ctx,
2554                                       struct smb2_tree *tree,
2555                                       struct smb2_handle fh,
2556                                       bool set)
2557 {
2558         union smb_ioctl ioctl;
2559         NTSTATUS status;
2560         uint8_t set_sparse;
2561
2562         ZERO_STRUCT(ioctl);
2563         ioctl.smb2.level = RAW_IOCTL_SMB2;
2564         ioctl.smb2.in.file.handle = fh;
2565         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2566         ioctl.smb2.in.max_response_size = 0;
2567         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2568         set_sparse = (set ? 0xFF : 0x0);
2569         ioctl.smb2.in.out.data = &set_sparse;
2570         ioctl.smb2.in.out.length = sizeof(set_sparse);
2571
2572         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2573         return status;
2574 }
2575
2576 static NTSTATUS test_sparse_get(struct torture_context *torture,
2577                                 TALLOC_CTX *mem_ctx,
2578                                 struct smb2_tree *tree,
2579                                 struct smb2_handle fh,
2580                                 bool *_is_sparse)
2581 {
2582         union smb_fileinfo io;
2583         NTSTATUS status;
2584
2585         ZERO_STRUCT(io);
2586         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2587         io.generic.in.file.handle = fh;
2588         status = smb2_getinfo_file(tree, mem_ctx, &io);
2589         if (!NT_STATUS_IS_OK(status)) {
2590                 return status;
2591         }
2592         *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2593
2594         return status;
2595 }
2596
2597 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2598                                         struct smb2_tree *tree)
2599 {
2600         struct smb2_handle fh;
2601         union smb_fileinfo io;
2602         NTSTATUS status;
2603         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2604         bool ok;
2605         bool is_sparse;
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 file");
2611
2612         status = test_ioctl_sparse_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, "Sparse files 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_SPARSE) == 0),
2628                        "sparse attr before set");
2629
2630         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2631         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2632
2633         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2634         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2635         torture_assert(torture, is_sparse, "no sparse attr after set");
2636
2637         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2638         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2639
2640         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2641         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2642         torture_assert(torture, !is_sparse, "sparse attr after unset");
2643
2644         smb2_util_close(tree, fh);
2645         talloc_free(tmp_ctx);
2646         return true;
2647 }
2648
2649 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2650                                         struct smb2_tree *tree)
2651 {
2652         struct smb2_handle fh;
2653         NTSTATUS status;
2654         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2655         bool ok;
2656         bool is_sparse;
2657
2658         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2659                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2660                         (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2661         torture_assert(torture, ok, "setup file");
2662
2663         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2664                                                 &ok);
2665         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2666         if (!ok) {
2667                 smb2_util_close(tree, fh);
2668                 torture_skip(torture, "Sparse files not supported\n");
2669         }
2670
2671         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2672         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2673         torture_assert(torture, !is_sparse, "sparse attr on open");
2674
2675         smb2_util_close(tree, fh);
2676         talloc_free(tmp_ctx);
2677         return true;
2678 }
2679
2680 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2681                                         struct smb2_tree *tree)
2682 {
2683         struct smb2_handle dirh;
2684         NTSTATUS status;
2685         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2686         bool ok;
2687
2688         smb2_deltree(tree, DNAME);
2689         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2690                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2691                                     FILE_ATTRIBUTE_DIRECTORY);
2692         torture_assert(torture, ok, "setup sparse directory");
2693
2694         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &dirh,
2695                                                 &ok);
2696         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2697         if (!ok) {
2698                 smb2_util_close(tree, dirh);
2699                 smb2_deltree(tree, DNAME);
2700                 torture_skip(torture, "Sparse files not supported\n");
2701         }
2702
2703         /* set sparse dir should fail, check for 2k12 & 2k8 response */
2704         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2705         torture_assert_ntstatus_equal(torture, status,
2706                                       NT_STATUS_INVALID_PARAMETER,
2707                                       "dir FSCTL_SET_SPARSE status");
2708
2709         smb2_util_close(tree, dirh);
2710         smb2_deltree(tree, DNAME);
2711         talloc_free(tmp_ctx);
2712         return true;
2713 }
2714
2715 /*
2716  * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2717  * buffer to indicate whether the flag should be set or cleared. When sent
2718  * without a buffer, it must be handled as if SetSparse=TRUE.
2719  */
2720 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2721                                         struct smb2_tree *tree)
2722 {
2723         struct smb2_handle fh;
2724         union smb_ioctl ioctl;
2725         NTSTATUS status;
2726         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2727         bool ok;
2728         bool is_sparse;
2729
2730         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2731                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2732                                     FILE_ATTRIBUTE_NORMAL);
2733         torture_assert(torture, ok, "setup file");
2734
2735         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2736                                                 &ok);
2737         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2738         if (!ok) {
2739                 smb2_util_close(tree, fh);
2740                 torture_skip(torture, "Sparse files not supported\n");
2741         }
2742
2743         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2744         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2745         torture_assert(torture, !is_sparse, "sparse attr before set");
2746
2747         ZERO_STRUCT(ioctl);
2748         ioctl.smb2.level = RAW_IOCTL_SMB2;
2749         ioctl.smb2.in.file.handle = fh;
2750         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2751         ioctl.smb2.in.max_response_size = 0;
2752         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2753         /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2754
2755         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2756         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2757
2758         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2759         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2760         torture_assert(torture, is_sparse, "no sparse attr after set");
2761
2762         /* second non-SetSparse request shouldn't toggle sparse */
2763         ZERO_STRUCT(ioctl);
2764         ioctl.smb2.level = RAW_IOCTL_SMB2;
2765         ioctl.smb2.in.file.handle = fh;
2766         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2767         ioctl.smb2.in.max_response_size = 0;
2768         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2769
2770         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2771         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2772
2773         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2774         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2775         torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2776
2777         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2778         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2779
2780         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2781         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2782         torture_assert(torture, !is_sparse, "sparse attr after unset");
2783
2784         smb2_util_close(tree, fh);
2785         talloc_free(tmp_ctx);
2786         return true;
2787 }
2788
2789 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2790                                            struct smb2_tree *tree)
2791 {
2792         struct smb2_handle fh;
2793         union smb_ioctl ioctl;
2794         NTSTATUS status;
2795         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2796         bool ok;
2797         bool is_sparse;
2798         uint8_t buf[100];
2799
2800         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2801                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2802                                     FILE_ATTRIBUTE_NORMAL);
2803         torture_assert(torture, ok, "setup file");
2804
2805         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2806                                                 &ok);
2807         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2808         if (!ok) {
2809                 smb2_util_close(tree, fh);
2810                 torture_skip(torture, "Sparse files not supported\n");
2811         }
2812
2813         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2814         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2815         torture_assert(torture, !is_sparse, "sparse attr before set");
2816
2817         ZERO_STRUCT(ioctl);
2818         ioctl.smb2.level = RAW_IOCTL_SMB2;
2819         ioctl.smb2.in.file.handle = fh;
2820         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2821         ioctl.smb2.in.max_response_size = 0;
2822         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2823
2824         /*
2825          * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2826          * Windows still successfully processes the request.
2827          */
2828         ZERO_ARRAY(buf);
2829         buf[0] = 0xFF; /* attempt to set sparse */
2830         ioctl.smb2.in.out.data = buf;
2831         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2832
2833         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2834         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2835
2836         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2837         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2838         torture_assert(torture, is_sparse, "no sparse attr after set");
2839
2840         ZERO_STRUCT(ioctl);
2841         ioctl.smb2.level = RAW_IOCTL_SMB2;
2842         ioctl.smb2.in.file.handle = fh;
2843         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2844         ioctl.smb2.in.max_response_size = 0;
2845         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2846
2847         ZERO_ARRAY(buf); /* clear sparse */
2848         ioctl.smb2.in.out.data = buf;
2849         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2850
2851         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2852         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2853
2854         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2855         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2856         torture_assert(torture, !is_sparse, "sparse attr after clear");
2857
2858         smb2_util_close(tree, fh);
2859         talloc_free(tmp_ctx);
2860         return true;
2861 }
2862
2863 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
2864                                    TALLOC_CTX *mem_ctx,
2865                                    struct smb2_tree *tree,
2866                                    struct smb2_handle fh,
2867                                    int64_t req_off,
2868                                    int64_t req_len,
2869                                    struct file_alloced_range_buf **_rsp,
2870                                    uint64_t *_rsp_count)
2871 {
2872         union smb_ioctl ioctl;
2873         NTSTATUS status;
2874         enum ndr_err_code ndr_ret;
2875         struct file_alloced_range_buf far_buf;
2876         struct file_alloced_range_buf *far_rsp = NULL;
2877         uint64_t far_count = 0;
2878         int i;
2879         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2880         if (tmp_ctx == NULL) {
2881                 return NT_STATUS_NO_MEMORY;
2882         }
2883
2884         ZERO_STRUCT(ioctl);
2885         ioctl.smb2.level = RAW_IOCTL_SMB2;
2886         ioctl.smb2.in.file.handle = fh;
2887         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
2888         ioctl.smb2.in.max_response_size = 1024;
2889         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2890
2891         far_buf.file_off = req_off;
2892         far_buf.len = req_len;
2893
2894         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
2895                                        &far_buf,
2896                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
2897         if (ndr_ret != NDR_ERR_SUCCESS) {
2898                 status = NT_STATUS_UNSUCCESSFUL;
2899                 goto err_out;
2900         }
2901
2902         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2903         if (!NT_STATUS_IS_OK(status)) {
2904                 goto err_out;
2905         }
2906
2907         if (ioctl.smb2.out.out.length == 0) {
2908                 goto done;
2909         }
2910
2911         if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
2912                 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
2913                                 ioctl.smb2.out.out.length);
2914                 status = NT_STATUS_INVALID_VIEW_SIZE;
2915                 goto err_out;
2916         }
2917
2918         far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
2919         far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
2920                                far_count);
2921         if (far_rsp == NULL) {
2922                 status = NT_STATUS_NO_MEMORY;
2923                 goto err_out;
2924         }
2925
2926         for (i = 0; i < far_count; i++) {
2927                 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
2928                                                &far_rsp[i],
2929                         (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
2930                 if (ndr_ret != NDR_ERR_SUCCESS) {
2931                         status = NT_STATUS_UNSUCCESSFUL;
2932                         goto err_out;
2933                 }
2934                 /* move to next buffer */
2935                 ioctl.smb2.out.out.data += sizeof(far_buf);
2936                 ioctl.smb2.out.out.length -= sizeof(far_buf);
2937         }
2938
2939 done:
2940         *_rsp = far_rsp;
2941         *_rsp_count = far_count;
2942         status = NT_STATUS_OK;
2943 err_out:
2944         talloc_free(tmp_ctx);
2945         return status;
2946 }
2947
2948 static bool test_ioctl_sparse_qar(struct torture_context *torture,
2949                                   struct smb2_tree *tree)
2950 {
2951         struct smb2_handle fh;
2952         NTSTATUS status;
2953         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2954         bool ok;
2955         bool is_sparse;
2956         struct file_alloced_range_buf *far_rsp = NULL;
2957         uint64_t far_count = 0;
2958
2959         /* zero length file, shouldn't have any ranges */
2960         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2961                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2962                                     FILE_ATTRIBUTE_NORMAL);
2963         torture_assert(torture, ok, "setup file");
2964
2965         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2966                                                 &ok);
2967         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2968         if (!ok) {
2969                 smb2_util_close(tree, fh);
2970                 torture_skip(torture, "Sparse files not supported\n");
2971         }
2972
2973         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2974         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2975         torture_assert(torture, !is_sparse, "sparse attr before set");
2976
2977         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2978                                     0,  /* off */
2979                                     0,  /* len */
2980                                     &far_rsp,
2981                                     &far_count);
2982         torture_assert_ntstatus_ok(torture, status,
2983                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2984         torture_assert_u64_equal(torture, far_count, 0,
2985                                  "unexpected response len");
2986
2987         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2988                                     0,  /* off */
2989                                     1024,       /* len */
2990                                     &far_rsp,
2991                                     &far_count);
2992         torture_assert_ntstatus_ok(torture, status,
2993                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2994         torture_assert_u64_equal(torture, far_count, 0,
2995                                  "unexpected response len");
2996
2997         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2998         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2999
3000         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3001         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3002         torture_assert(torture, is_sparse, "no sparse attr after set");
3003
3004         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3005                                     0,  /* off */
3006                                     1024,       /* len */
3007                                     &far_rsp,
3008                                     &far_count);
3009         torture_assert_ntstatus_ok(torture, status,
3010                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3011         torture_assert_u64_equal(torture, far_count, 0,
3012                                  "unexpected response len");
3013
3014         /* write into the (now) sparse file at 4k offset */
3015         ok = write_pattern(torture, tree, tmp_ctx, fh,
3016                            4096,        /* off */
3017                            1024,        /* len */
3018                            4096);       /* pattern offset */
3019         torture_assert(torture, ok, "write pattern");
3020
3021         /*
3022          * Query range before write off. Whether it's allocated or not is FS
3023          * dependent. NTFS deallocates chunks in 64K increments, but others
3024          * (e.g. XFS, Btrfs, etc.) may deallocate 4K chunks.
3025          */
3026         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3027                                     0,  /* off */
3028                                     4096,       /* len */
3029                                     &far_rsp,
3030                                     &far_count);
3031         torture_assert_ntstatus_ok(torture, status,
3032                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3033         if (far_count == 0) {
3034                 torture_comment(torture, "FS deallocated 4K chunk\n");
3035         } else {
3036                 /* expect fully allocated */
3037                 torture_assert_u64_equal(torture, far_count, 1,
3038                                          "unexpected response len");
3039                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3040                 torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
3041         }
3042
3043         /*
3044          * Query range before and past write, it should be allocated up to the
3045          * end of the write.
3046          */
3047         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3048                                     0,  /* off */
3049                                     8192,       /* len */
3050                                     &far_rsp,
3051                                     &far_count);
3052         torture_assert_ntstatus_ok(torture, status,
3053                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3054         torture_assert_u64_equal(torture, far_count, 1,
3055                                  "unexpected response len");
3056         /* FS dependent */
3057         if (far_rsp[0].file_off == 4096) {
3058                 /* 4K chunk unallocated */
3059                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 4096, "far offset");
3060                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024, "far len");
3061         } else {
3062                 /* expect fully allocated */
3063                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
3064                 torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
3065         }
3066
3067         smb2_util_close(tree, fh);
3068         talloc_free(tmp_ctx);
3069         return true;
3070 }
3071
3072 static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
3073                                             struct smb2_tree *tree)
3074 {
3075         struct smb2_handle fh;
3076         union smb_ioctl ioctl;
3077         struct file_alloced_range_buf far_buf;
3078         NTSTATUS status;
3079         enum ndr_err_code ndr_ret;
3080         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3081         bool ok;
3082         size_t old_len;
3083
3084         /* zero length file, shouldn't have any ranges */
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 file");
3089
3090         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3091                                                 &ok);
3092         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3093         if (!ok) {
3094                 smb2_util_close(tree, fh);
3095                 torture_skip(torture, "Sparse files not supported\n");
3096         }
3097
3098         /* no allocated ranges, no space for range response, should pass */
3099         ZERO_STRUCT(ioctl);
3100         ioctl.smb2.level = RAW_IOCTL_SMB2;
3101         ioctl.smb2.in.file.handle = fh;
3102         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
3103         ioctl.smb2.in.max_response_size = 0;
3104         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3105
3106         far_buf.file_off = 0;
3107         far_buf.len = 1024;
3108         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3109                                        &far_buf,
3110                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
3111         torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
3112
3113         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3114         torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_ALLOCATED_RANGES");
3115
3116         /* write into the file at 4k offset */
3117         ok = write_pattern(torture, tree, tmp_ctx, fh,
3118                            0,           /* off */
3119                            1024,        /* len */
3120                            0);          /* pattern offset */
3121         torture_assert(torture, ok, "write pattern");
3122
3123         /* allocated range, no space for range response, should fail */
3124         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3125         torture_assert_ntstatus_equal(torture, status,
3126                                       NT_STATUS_BUFFER_TOO_SMALL, "qar no space");
3127
3128         /* oversize (2x) file_alloced_range_buf in request, should pass */
3129         ioctl.smb2.in.max_response_size = 1024;
3130         old_len = ioctl.smb2.in.out.length;
3131         ok = data_blob_realloc(tmp_ctx, &ioctl.smb2.in.out,
3132                                (ioctl.smb2.in.out.length * 2));
3133         torture_assert(torture, ok, "2x data buffer");
3134         memcpy(ioctl.smb2.in.out.data + old_len, ioctl.smb2.in.out.data,
3135                old_len);
3136         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3137         torture_assert_ntstatus_ok(torture, status, "qar too big");
3138
3139         /* no file_alloced_range_buf in request, should fail */
3140         data_blob_free(&ioctl.smb2.in.out);
3141         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3142         torture_assert_ntstatus_equal(torture, status,
3143                                       NT_STATUS_INVALID_PARAMETER, "qar empty");
3144
3145         return true;
3146 }
3147
3148 /*
3149  * 2.3.57 FSCTL_SET_ZERO_DATA Request
3150  *
3151  * How an implementation zeros data within a file is implementation-dependent.
3152  * A file system MAY choose to deallocate regions of disk space that have been
3153  * zeroed.<50>
3154  * <50>
3155  * ... NTFS might deallocate disk space in the file if the file is stored on an
3156  * NTFS volume, and the file is sparse or compressed. It will free any allocated
3157  * space in chunks of 64 kilobytes that begin at an offset that is a multiple of
3158  * 64 kilobytes. Other bytes in the file (prior to the first freed 64-kilobyte
3159  * chunk and after the last freed 64-kilobyte chunk) will be zeroed but not
3160  * deallocated.
3161  */
3162 static NTSTATUS test_ioctl_zdata_req(struct torture_context *torture,
3163                                      TALLOC_CTX *mem_ctx,
3164                                      struct smb2_tree *tree,
3165                                      struct smb2_handle fh,
3166                                      int64_t off,
3167                                      int64_t beyond_final_zero)
3168 {
3169         union smb_ioctl ioctl;
3170         NTSTATUS status;
3171         enum ndr_err_code ndr_ret;
3172         struct file_zero_data_info zdata_info;
3173         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
3174         if (tmp_ctx == NULL) {
3175                 return NT_STATUS_NO_MEMORY;
3176         }
3177
3178         ZERO_STRUCT(ioctl);
3179         ioctl.smb2.level = RAW_IOCTL_SMB2;
3180         ioctl.smb2.in.file.handle = fh;
3181         ioctl.smb2.in.function = FSCTL_SET_ZERO_DATA;
3182         ioctl.smb2.in.max_response_size = 0;
3183         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
3184
3185         zdata_info.file_off = off;
3186         zdata_info.beyond_final_zero = beyond_final_zero;
3187
3188         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3189                                        &zdata_info,
3190                         (ndr_push_flags_fn_t)ndr_push_file_zero_data_info);
3191         if (ndr_ret != NDR_ERR_SUCCESS) {
3192                 status = NT_STATUS_UNSUCCESSFUL;
3193                 goto err_out;
3194         }
3195
3196         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3197         if (!NT_STATUS_IS_OK(status)) {
3198                 goto err_out;
3199         }
3200
3201         status = NT_STATUS_OK;
3202 err_out:
3203         talloc_free(tmp_ctx);
3204         return status;
3205 }
3206
3207 static bool test_ioctl_sparse_punch(struct torture_context *torture,
3208                                     struct smb2_tree *tree)
3209 {
3210         struct smb2_handle fh;
3211         NTSTATUS status;
3212         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3213         bool ok;
3214         bool is_sparse;
3215         struct file_alloced_range_buf *far_rsp = NULL;
3216         uint64_t far_count = 0;
3217
3218         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3219                                     FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3220                                     FILE_ATTRIBUTE_NORMAL);
3221         torture_assert(torture, ok, "setup file");
3222
3223         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3224                                                 &ok);
3225         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3226         if (!ok) {
3227                 smb2_util_close(tree, fh);
3228                 torture_skip(torture, "Sparse files not supported\n");
3229         }
3230
3231         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3232         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3233         torture_assert(torture, !is_sparse, "sparse attr before set");
3234
3235         /* zero (hole-punch) the data, without sparse flag */
3236         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3237                                       0,        /* off */
3238                                       4096);    /* beyond_final_zero */
3239         torture_assert_ntstatus_ok(torture, status, "zero_data");
3240
3241         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3242                                     0,          /* off */
3243                                     4096,       /* len */
3244                                     &far_rsp,
3245                                     &far_count);
3246         torture_assert_ntstatus_ok(torture, status,
3247                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3248         torture_assert_u64_equal(torture, far_count, 1,
3249                                  "unexpected response len");
3250
3251         /* expect fully allocated */
3252         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3253                                  "unexpected far off");
3254         torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3255                                  "unexpected far len");
3256         /* check that the data is now zeroed */
3257         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3258         torture_assert(torture, ok, "non-sparse zeroed range");
3259
3260         /* set sparse */
3261         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3262         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3263
3264         /* still fully allocated on NTFS, see note below for Samba */
3265         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3266                                     0,          /* off */
3267                                     4096,       /* len */
3268                                     &far_rsp,
3269                                     &far_count);
3270         torture_assert_ntstatus_ok(torture, status,
3271                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3272         /*
3273          * FS specific: Samba uses PUNCH_HOLE to zero the range, and
3274          * subsequently uses fallocate() to allocate the punched range if the
3275          * file is marked non-sparse and "strict allocate" is enabled. In both
3276          * cases, the zeroed range will not be detected by SEEK_DATA, so the
3277          * range won't be present in QAR responses until the file is marked
3278          * non-sparse again.
3279          */
3280         if (far_count == 0) {
3281                 torture_comment(torture, "non-sparse zeroed range disappeared "
3282                                 "after marking sparse\n");
3283         } else {
3284                 /* NTFS: range remains fully allocated */
3285                 torture_assert_u64_equal(torture, far_count, 1,
3286                                          "unexpected response len");
3287                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3288                                          "unexpected far off");
3289                 torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3290                                          "unexpected far len");
3291         }
3292
3293         /* zero (hole-punch) the data, _with_ sparse flag */
3294         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3295                                       0,        /* off */
3296                                       4096);    /* beyond_final_zero */
3297         torture_assert_ntstatus_ok(torture, status, "zero_data");
3298
3299         /* the range should no longer be alloced */
3300         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3301                                     0,          /* off */
3302                                     4096,       /* len */
3303                                     &far_rsp,
3304                                     &far_count);
3305         torture_assert_ntstatus_ok(torture, status,
3306                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3307         torture_assert_u64_equal(torture, far_count, 0,
3308                                  "unexpected response len");
3309
3310         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3311         torture_assert(torture, ok, "sparse zeroed range");
3312
3313         /* remove sparse flag, this should "unsparse" the zeroed range */
3314         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3315         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3316
3317         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3318                                     0,          /* off */
3319                                     4096,       /* len */
3320                                     &far_rsp,
3321                                     &far_count);
3322         torture_assert_ntstatus_ok(torture, status,
3323                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3324         torture_assert_u64_equal(torture, far_count, 1,
3325                                  "unexpected response len");
3326         /* expect fully allocated */
3327         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3328                                  "unexpected far off");
3329         torture_assert_u64_equal(torture, far_rsp[0].len, 4096,
3330                                  "unexpected far len");
3331
3332         ok = check_zero(torture, tree, tmp_ctx, fh, 0, 4096);
3333         torture_assert(torture, ok, "sparse zeroed range");
3334
3335         smb2_util_close(tree, fh);
3336         talloc_free(tmp_ctx);
3337         return true;
3338 }
3339
3340 /*
3341  * Find the point at which a zeroed range in a sparse file is deallocated by the
3342  * underlying filesystem. NTFS on Windows Server 2012 deallocates chunks in 64k
3343  * increments. Also check whether zeroed neighbours are merged for deallocation.
3344  */
3345 static bool test_ioctl_sparse_hole_dealloc(struct torture_context *torture,
3346                                            struct smb2_tree *tree)
3347 {
3348         struct smb2_handle fh;
3349         NTSTATUS status;
3350         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3351         bool ok;
3352         uint64_t file_size;
3353         uint64_t hlen;
3354         uint64_t dealloc_chunk_len = 0;
3355         struct file_alloced_range_buf *far_rsp = NULL;
3356         uint64_t far_count = 0;
3357
3358         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3359                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3360                                     FILE_ATTRIBUTE_NORMAL);
3361         torture_assert(torture, ok, "setup file 1");
3362
3363         /* check for FS sparse file */
3364         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3365                                                 &ok);
3366         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3367         if (!ok) {
3368                 smb2_util_close(tree, fh);
3369                 torture_skip(torture, "Sparse files not supported\n");
3370         }
3371
3372         /* set sparse */
3373         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3374         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3375
3376         file_size = 1024 * 1024;
3377
3378         ok = write_pattern(torture, tree, tmp_ctx, fh,
3379                            0,           /* off */
3380                            file_size,   /* len */
3381                            0);  /* pattern offset */
3382         torture_assert(torture, ok, "write pattern");
3383
3384          /* check allocated ranges, should be fully allocated */
3385         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3386                                     0,                  /* off */
3387                                     file_size,          /* len */
3388                                     &far_rsp,
3389                                     &far_count);
3390         torture_assert_ntstatus_ok(torture, status,
3391                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3392         torture_assert_u64_equal(torture, far_count, 1,
3393                                  "unexpected response len");
3394         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3395                                  "unexpected far off");
3396         torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3397                                  "unexpected far len");
3398
3399         /* punch holes in sizes of 1k increments */
3400         for (hlen = 0; hlen <= file_size; hlen += 4096) {
3401
3402                 /* punch a hole from zero to the current increment */
3403                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3404                                               0,        /* off */
3405                                               hlen);    /* beyond_final_zero */
3406                 torture_assert_ntstatus_ok(torture, status, "zero_data");
3407
3408                 /* ensure hole is zeroed, and pattern is consistent */
3409                 ok = check_zero(torture, tree, tmp_ctx, fh, 0, hlen);
3410                 torture_assert(torture, ok, "sparse zeroed range");
3411
3412                 ok = check_pattern(torture, tree, tmp_ctx, fh, hlen,
3413                                    file_size - hlen, hlen);
3414                 torture_assert(torture, ok, "allocated pattern range");
3415
3416                  /* Check allocated ranges, hole might have been deallocated */
3417                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3418                                             0,          /* off */
3419                                             file_size,  /* len */
3420                                             &far_rsp,
3421                                             &far_count);
3422                 torture_assert_ntstatus_ok(torture, status,
3423                                            "FSCTL_QUERY_ALLOCATED_RANGES");
3424                 if ((hlen == file_size) && (far_count == 0)) {
3425                         /* hole covered entire file, deallocation occurred */
3426                         dealloc_chunk_len = file_size;
3427                         break;
3428                 }
3429
3430                 torture_assert_u64_equal(torture, far_count, 1,
3431                                          "unexpected response len");
3432                 if (far_rsp[0].file_off != 0) {
3433                         /*
3434                          * We now know the hole punch length needed to trigger a
3435                          * deallocation on this FS...
3436                          */
3437                         dealloc_chunk_len = hlen;
3438                         torture_comment(torture, "hole punch %lu@0 resulted in "
3439                                         "deallocation of %lu@0\n", hlen,
3440                                         far_rsp[0].file_off);
3441                         torture_assert_u64_equal(torture,
3442                                                  file_size - far_rsp[0].len,
3443                                                  far_rsp[0].file_off,
3444                                                  "invalid alloced range");
3445                         break;
3446                 }
3447         }
3448
3449         if (dealloc_chunk_len == 0) {
3450                 torture_comment(torture, "strange, this FS never deallocates"
3451                                 "zeroed ranges in sparse files\n");
3452                 return true;    /* FS specific, not a failure */
3453         }
3454
3455         /*
3456          * Check whether deallocation occurs when the (now known)
3457          * deallocation chunk size is punched via two ZERO_DATA requests.
3458          * I.e. Does the FS merge the two ranges and deallocate the chunk?
3459          * NTFS on Windows Server 2012 does not.
3460          */
3461         ok = write_pattern(torture, tree, tmp_ctx, fh,
3462                            0,           /* off */
3463                            file_size,   /* len */
3464                            0);  /* pattern offset */
3465         torture_assert(torture, ok, "write pattern");
3466
3467         /* divide dealloc chunk size by two, to use as punch length */
3468         hlen = dealloc_chunk_len >> 1;
3469
3470         /*
3471          *                     /half of dealloc chunk size           1M\
3472          *                     |                                       |
3473          * /offset 0           |                   /dealloc chunk size |
3474          * |------------------ |-------------------|-------------------|
3475          * | zeroed, 1st punch | zeroed, 2nd punch | existing pattern  |
3476          */
3477         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3478                                       0,        /* off */
3479                                       hlen);    /* beyond final zero */
3480         torture_assert_ntstatus_ok(torture, status, "zero_data");
3481
3482         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3483                                       hlen,     /* off */
3484                                       dealloc_chunk_len); /* beyond final */
3485         torture_assert_ntstatus_ok(torture, status, "zero_data");
3486
3487         /* ensure holes are zeroed, and pattern is consistent */
3488         ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3489         torture_assert(torture, ok, "sparse zeroed range");
3490
3491         ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3492                            file_size - dealloc_chunk_len, dealloc_chunk_len);
3493         torture_assert(torture, ok, "allocated pattern range");
3494
3495         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3496                                     0,                  /* off */
3497                                     file_size,          /* len */
3498                                     &far_rsp,
3499                                     &far_count);
3500         torture_assert_ntstatus_ok(torture, status,
3501                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3502
3503         if ((far_count == 0) && (dealloc_chunk_len == file_size)) {
3504                 torture_comment(torture, "holes merged for deallocation of "
3505                                 "full file\n");
3506                 return true;
3507         }
3508         torture_assert_u64_equal(torture, far_count, 1,
3509                                  "unexpected response len");
3510         if (far_rsp[0].file_off == dealloc_chunk_len) {
3511                 torture_comment(torture, "holes merged for deallocation of "
3512                                 "%lu chunk\n", dealloc_chunk_len);
3513                 torture_assert_u64_equal(torture,
3514                                          file_size - far_rsp[0].len,
3515                                          far_rsp[0].file_off,
3516                                          "invalid alloced range");
3517         } else {
3518                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3519                                          "unexpected deallocation");
3520                 torture_comment(torture, "holes not merged for deallocation\n");
3521         }
3522
3523         smb2_util_close(tree, fh);
3524
3525         /*
3526          * Check whether an unwritten range is allocated when a sparse file is
3527          * written to at an offset past the dealloc chunk size:
3528          *
3529          *                     /dealloc chunk size
3530          * /offset 0           |
3531          * |------------------ |-------------------|
3532          * |     unwritten     |      pattern      |
3533          */
3534         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3535                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3536                                     FILE_ATTRIBUTE_NORMAL);
3537         torture_assert(torture, ok, "setup file 1");
3538
3539         /* set sparse */
3540         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3541         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3542
3543         ok = write_pattern(torture, tree, tmp_ctx, fh,
3544                            dealloc_chunk_len,   /* off */
3545                            1024,                /* len */
3546                            dealloc_chunk_len);  /* pattern offset */
3547         torture_assert(torture, ok, "write pattern");
3548
3549         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3550                                     0,                          /* off */
3551                                     dealloc_chunk_len + 1024,   /* len */
3552                                     &far_rsp,
3553                                     &far_count);
3554         torture_assert_ntstatus_ok(torture, status,
3555                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3556         torture_assert_u64_equal(torture, far_count, 1,
3557                                  "unexpected response len");
3558         if (far_rsp[0].file_off == 0) {
3559                 torture_assert_u64_equal(torture, far_rsp[0].len,
3560                                          dealloc_chunk_len + 1024,
3561                                          "unexpected far len");
3562                 torture_comment(torture, "unwritten range fully allocated\n");
3563         } else {
3564                 torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3565                                          "unexpected deallocation");
3566                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3567                                          "unexpected far len");
3568                 torture_comment(torture, "unwritten range not allocated\n");
3569         }
3570
3571         ok = check_zero(torture, tree, tmp_ctx, fh, 0, dealloc_chunk_len);
3572         torture_assert(torture, ok, "sparse zeroed range");
3573
3574         ok = check_pattern(torture, tree, tmp_ctx, fh, dealloc_chunk_len,
3575                            1024, dealloc_chunk_len);
3576         torture_assert(torture, ok, "allocated pattern range");
3577
3578         /* unsparse, should now be fully allocated */
3579         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
3580         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3581
3582         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3583                                     0,                          /* off */
3584                                     dealloc_chunk_len + 1024,   /* len */
3585                                     &far_rsp,
3586                                     &far_count);
3587         torture_assert_ntstatus_ok(torture, status,
3588                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3589         torture_assert_u64_equal(torture, far_count, 1,
3590                                  "unexpected response len");
3591         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3592                                  "unexpected deallocation");
3593         torture_assert_u64_equal(torture, far_rsp[0].len,
3594                                  dealloc_chunk_len + 1024,
3595                                  "unexpected far len");
3596
3597         smb2_util_close(tree, fh);
3598         talloc_free(tmp_ctx);
3599         return true;
3600 }
3601
3602 /* check whether a file with compression and sparse attrs can be deallocated */
3603 static bool test_ioctl_sparse_compressed(struct torture_context *torture,
3604                                          struct smb2_tree *tree)
3605 {
3606         struct smb2_handle fh;
3607         NTSTATUS status;
3608         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3609         bool ok;
3610         uint64_t file_size = 1024 * 1024;
3611         struct file_alloced_range_buf *far_rsp = NULL;
3612         uint64_t far_count = 0;
3613
3614         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3615                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
3616                                     FILE_ATTRIBUTE_NORMAL);
3617         torture_assert(torture, ok, "setup file 1");
3618
3619         /* check for FS sparse file and compression support */
3620         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3621                                                 &ok);
3622         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3623         if (!ok) {
3624                 smb2_util_close(tree, fh);
3625                 torture_skip(torture, "Sparse files not supported\n");
3626         }
3627
3628         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
3629                                                   &ok);
3630         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3631         if (!ok) {
3632                 smb2_util_close(tree, fh);
3633                 torture_skip(torture, "FS compression not supported\n");
3634         }
3635
3636         /* set compression and write some data */
3637         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
3638                                          COMPRESSION_FORMAT_DEFAULT);
3639         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
3640
3641         ok = write_pattern(torture, tree, tmp_ctx, fh,
3642                            0,           /* off */
3643                            file_size,   /* len */
3644                            0);          /* pattern offset */
3645         torture_assert(torture, ok, "write pattern");
3646
3647         /* set sparse - now sparse and compressed */
3648         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
3649         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3650
3651          /* check allocated ranges, should be fully alloced */
3652         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3653                                     0,          /* off */
3654                                     file_size,  /* len */
3655                                     &far_rsp,
3656                                     &far_count);
3657         torture_assert_ntstatus_ok(torture, status,
3658                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3659         torture_assert_u64_equal(torture, far_count, 1,
3660                                  "unexpected response len");
3661         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3662                                  "unexpected far off");
3663         torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3664                                  "unexpected far len");
3665
3666         /* zero (hole-punch) all data, with sparse and compressed attrs */
3667         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3668                                       0,                /* off */
3669                                       file_size);       /* beyond_final_zero */
3670         torture_assert_ntstatus_ok(torture, status, "zero_data");
3671
3672          /*
3673           * Windows Server 2012 still deallocates a zeroed range when a sparse
3674           * file carries the compression attribute.
3675           */
3676         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3677                                     0,          /* off */
3678                                     file_size,  /* len */
3679                                     &far_rsp,
3680                                     &far_count);
3681         torture_assert_ntstatus_ok(torture, status,
3682                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3683         if (far_count == 0) {
3684                 torture_comment(torture, "sparse & compressed file "
3685                                 "deallocated after hole-punch\n");
3686         } else {
3687                 torture_assert_u64_equal(torture, far_count, 1,
3688                                          "unexpected response len");
3689                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3690                                          "unexpected far off");
3691                 torture_assert_u64_equal(torture, far_rsp[0].len, file_size,
3692                                          "unexpected far len");
3693                 torture_comment(torture, "sparse & compressed file fully "
3694                                 "allocated after hole-punch\n");
3695         }
3696
3697         smb2_util_close(tree, fh);
3698         talloc_free(tmp_ctx);
3699         return true;
3700 }
3701
3702 /*
3703  * Create a sparse file, then attempt to copy unallocated and allocated ranges
3704  * into a target file using FSCTL_SRV_COPYCHUNK.
3705  */
3706 static bool test_ioctl_sparse_copy_chunk(struct torture_context *torture,
3707                                          struct smb2_tree *tree)
3708 {
3709         struct smb2_handle src_h;
3710         struct smb2_handle dest_h;
3711         NTSTATUS status;
3712         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3713         bool ok;
3714         uint64_t dealloc_chunk_len = 64 * 1024; /* Windows 2012 */
3715         struct file_alloced_range_buf *far_rsp = NULL;
3716         uint64_t far_count = 0;
3717         union smb_ioctl ioctl;
3718         struct srv_copychunk_copy cc_copy;
3719         struct srv_copychunk_rsp cc_rsp;
3720         enum ndr_err_code ndr_ret;
3721
3722         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3723                                     FNAME, &src_h, 0, SEC_RIGHTS_FILE_ALL,
3724                                     FILE_ATTRIBUTE_NORMAL);
3725         torture_assert(torture, ok, "setup file");
3726
3727         /* check for FS sparse file support */
3728         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &src_h,
3729                                                 &ok);
3730         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3731         smb2_util_close(tree, src_h);
3732         if (!ok) {
3733                 torture_skip(torture, "Sparse files not supported\n");
3734         }
3735
3736         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
3737                                    1, /* chunks */
3738                                    &src_h, 0, /* src file */
3739                                    SEC_RIGHTS_FILE_ALL,
3740                                    &dest_h, 0,  /* dest file */
3741                                    SEC_RIGHTS_FILE_ALL,
3742                                    &cc_copy,
3743                                    &ioctl);
3744         torture_assert(torture, ok, "setup copy chunk error");
3745
3746         /* set sparse */
3747         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, src_h, true);
3748         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3749
3750         /* start after dealloc_chunk_len, to create an unwritten sparse range */
3751         ok = write_pattern(torture, tree, tmp_ctx, src_h,
3752                            dealloc_chunk_len,   /* off */
3753                            1024,        /* len */
3754                            dealloc_chunk_len);  /* pattern offset */
3755         torture_assert(torture, ok, "write pattern");
3756
3757          /* Skip test if 64k chunk is allocated - FS specific */
3758         status = test_ioctl_qar_req(torture, tmp_ctx, tree, src_h,
3759                                     0,                          /* off */
3760                                     dealloc_chunk_len + 1024,   /* len */
3761                                     &far_rsp,
3762                                     &far_count);
3763         torture_assert_ntstatus_ok(torture, status,
3764                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3765         torture_assert_u64_equal(torture, far_count, 1,
3766                                  "unexpected response len");
3767         if (far_rsp[0].file_off == 0) {
3768                 torture_skip(torture, "unwritten range fully allocated\n");
3769         }
3770
3771         torture_assert_u64_equal(torture, far_rsp[0].file_off, dealloc_chunk_len,
3772                                  "unexpected allocation");
3773         torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3774                                  "unexpected far len");
3775
3776         /* copy-chunk unallocated + written ranges into non-sparse dest */
3777
3778         cc_copy.chunks[0].source_off = 0;
3779         cc_copy.chunks[0].target_off = 0;
3780         cc_copy.chunks[0].length = dealloc_chunk_len + 1024;
3781
3782         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
3783                                        &cc_copy,
3784                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
3785         torture_assert_ndr_success(torture, ndr_ret,
3786                                    "ndr_push_srv_copychunk_copy");
3787
3788         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3789         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3790
3791         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3792                                        &cc_rsp,
3793                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3794         torture_assert_ndr_success(torture, ndr_ret,
3795                                    "ndr_pull_srv_copychunk_rsp");
3796
3797         ok = check_copy_chunk_rsp(torture, &cc_rsp,
3798                                   1,    /* chunks written */
3799                                   0,    /* chunk bytes unsuccessfully written */
3800                                   dealloc_chunk_len + 1024); /* bytes written */
3801         torture_assert(torture, ok, "bad copy chunk response data");
3802
3803         ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3804         torture_assert(torture, ok, "sparse zeroed range");
3805
3806         ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3807                            1024, dealloc_chunk_len);
3808         torture_assert(torture, ok, "copychunked range");
3809
3810         /* copied range should be allocated in non-sparse dest */
3811         status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3812                                     0,                          /* off */
3813                                     dealloc_chunk_len + 1024,   /* len */
3814                                     &far_rsp,
3815                                     &far_count);
3816         torture_assert_ntstatus_ok(torture, status,
3817                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3818         torture_assert_u64_equal(torture, far_count, 1,
3819                                  "unexpected response len");
3820         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3821                                  "unexpected allocation");
3822         torture_assert_u64_equal(torture, far_rsp[0].len,
3823                                  dealloc_chunk_len + 1024,
3824                                  "unexpected far len");
3825
3826         /* set dest as sparse */
3827         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dest_h, true);
3828         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
3829
3830         /* zero (hole-punch) all data */
3831         status = test_ioctl_zdata_req(torture, tmp_ctx, tree, dest_h,
3832                                       0,                /* off */
3833                                       dealloc_chunk_len + 1024);
3834         torture_assert_ntstatus_ok(torture, status, "zero_data");
3835
3836         /* zeroed range might be deallocated */
3837         status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3838                                     0,                          /* off */
3839                                     dealloc_chunk_len + 1024,   /* len */
3840                                     &far_rsp,
3841                                     &far_count);
3842         torture_assert_ntstatus_ok(torture, status,
3843                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3844         if (far_count == 0) {
3845                 /* FS specific (e.g. NTFS) */
3846                 torture_comment(torture, "FS deallocates file on full-range "
3847                                 "punch\n");
3848         } else {
3849                 /* FS specific (e.g. EXT4) */
3850                 torture_comment(torture, "FS doesn't deallocate file on "
3851                                 "full-range punch\n");
3852         }
3853         ok = check_zero(torture, tree, tmp_ctx, dest_h, 0,
3854                         dealloc_chunk_len + 1024);
3855         torture_assert(torture, ok, "punched zeroed range");
3856
3857         /* copy-chunk again, this time with sparse dest */
3858         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
3859         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
3860
3861         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
3862                                        &cc_rsp,
3863                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
3864         torture_assert_ndr_success(torture, ndr_ret,
3865                                    "ndr_pull_srv_copychunk_rsp");
3866
3867         ok = check_copy_chunk_rsp(torture, &cc_rsp,
3868                                   1,    /* chunks written */
3869                                   0,    /* chunk bytes unsuccessfully written */
3870                                   dealloc_chunk_len + 1024); /* bytes written */
3871         torture_assert(torture, ok, "bad copy chunk response data");
3872
3873         ok = check_zero(torture, tree, tmp_ctx, dest_h, 0, dealloc_chunk_len);
3874         torture_assert(torture, ok, "sparse zeroed range");
3875
3876         ok = check_pattern(torture, tree, tmp_ctx, dest_h, dealloc_chunk_len,
3877                            1024, dealloc_chunk_len);
3878         torture_assert(torture, ok, "copychunked range");
3879
3880         /* copied range may be allocated in sparse dest */
3881         status = test_ioctl_qar_req(torture, tmp_ctx, tree, dest_h,
3882                                     0,                          /* off */
3883                                     dealloc_chunk_len + 1024,   /* len */
3884                                     &far_rsp,
3885                                     &far_count);
3886         torture_assert_ntstatus_ok(torture, status,
3887                         "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3888         torture_assert_u64_equal(torture, far_count, 1,
3889                                  "unexpected response len");
3890         /*
3891          * FS specific: sparse region may be unallocated in dest if copy-chunk
3892          *              is handled in a sparse preserving way - E.g. vfs_btrfs
3893          *              with BTRFS_IOC_CLONE_RANGE.
3894          */
3895         if (far_rsp[0].file_off == dealloc_chunk_len) {
3896                 torture_comment(torture, "copy-chunk sparse range preserved\n");
3897                 torture_assert_u64_equal(torture, far_rsp[0].len, 1024,
3898                                          "unexpected far len");
3899         } else {
3900                 torture_assert_u64_equal(torture, far_rsp[0].file_off, 0,
3901                                          "unexpected allocation");
3902                 torture_assert_u64_equal(torture, far_rsp[0].len,
3903                                          dealloc_chunk_len + 1024,
3904                                          "unexpected far len");
3905         }
3906
3907         smb2_util_close(tree, src_h);
3908         smb2_util_close(tree, dest_h);
3909         talloc_free(tmp_ctx);
3910         return true;
3911 }
3912
3913 static bool test_ioctl_sparse_punch_invalid(struct torture_context *torture,
3914                                             struct smb2_tree *tree)
3915 {
3916         struct smb2_handle fh;
3917         NTSTATUS status;
3918         TALLOC_CTX *tmp_ctx = talloc_new(tree);
3919         bool ok;
3920         bool is_sparse;
3921         int i;
3922
3923         ok = test_setup_create_fill(torture, tree, tmp_ctx,
3924                                     FNAME, &fh, 4096, SEC_RIGHTS_FILE_ALL,
3925                                     FILE_ATTRIBUTE_NORMAL);
3926         torture_assert(torture, ok, "setup file");
3927
3928         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
3929                                                 &ok);
3930         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
3931         if (!ok) {
3932                 smb2_util_close(tree, fh);
3933                 torture_skip(torture, "Sparse files not supported\n");
3934         }
3935
3936         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
3937         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
3938         torture_assert(torture, !is_sparse, "sparse attr before set");
3939
3940         /* loop twice, without and with sparse attrib */
3941         for (i = 0; i <= 1;  i++) {
3942                 union smb_fileinfo io;
3943                 struct file_alloced_range_buf *far_rsp = NULL;
3944                 uint64_t far_count = 0;
3945
3946                 /* get size before & after. zero data should never change it */
3947                 ZERO_STRUCT(io);
3948                 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3949                 io.generic.in.file.handle = fh;
3950                 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3951                 torture_assert_ntstatus_ok(torture, status, "getinfo");
3952                 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
3953                                          4096, "size after IO");
3954
3955                 /* valid 8 byte zero data, but after EOF */
3956                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3957                                               4096,     /* off */
3958                                               4104);    /* beyond_final_zero */
3959                 torture_assert_ntstatus_ok(torture, status, "zero_data");
3960
3961                 /* valid 8 byte zero data, but after EOF */
3962                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3963                                               8192,     /* off */
3964                                               8200);    /* beyond_final_zero */
3965                 torture_assert_ntstatus_ok(torture, status, "zero_data");
3966
3967                 ZERO_STRUCT(io);
3968                 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
3969                 io.generic.in.file.handle = fh;
3970                 status = smb2_getinfo_file(tree, tmp_ctx, &io);
3971                 torture_assert_ntstatus_ok(torture, status, "getinfo");
3972                 torture_assert_int_equal(torture, (int)io.all_info2.out.size,
3973                                          4096, "size after IO");
3974
3975                 /* valid 0 byte zero data, without sparse flag */
3976                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3977                                               4095,     /* off */
3978                                               4095);    /* beyond_final_zero */
3979                 torture_assert_ntstatus_ok(torture, status, "zero_data");
3980
3981                 /* INVALID off is past beyond_final_zero */
3982                 status = test_ioctl_zdata_req(torture, tmp_ctx, tree, fh,
3983                                               4096,     /* off */
3984                                               4095);    /* beyond_final_zero */
3985                 torture_assert_ntstatus_equal(torture, status,
3986                                               NT_STATUS_INVALID_PARAMETER,
3987                                               "invalid zero_data");
3988
3989                 /* zero length QAR - valid */
3990                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
3991                                             0,                  /* off */
3992                                             0,                  /* len */
3993                                             &far_rsp, &far_count);
3994                 torture_assert_ntstatus_ok(torture, status,
3995                                 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
3996                 torture_assert_u64_equal(torture, far_count, 0,
3997                                          "unexpected response len");
3998
3999                 /* QAR after EOF - valid */
4000                 status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
4001                                             4096,               /* off */
4002                                             1024,               /* len */
4003                                             &far_rsp, &far_count);
4004                 torture_assert_ntstatus_ok(torture, status,
4005                                 "FSCTL_QUERY_ALLOCATED_RANGES req failed");
4006                 torture_assert_u64_equal(torture, far_count, 0,
4007                                          "unexpected response len");
4008
4009                 /* set sparse */
4010                 status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh,
4011                                                true);
4012                 torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
4013         }
4014
4015         smb2_util_close(tree, fh);
4016         talloc_free(tmp_ctx);
4017         return true;
4018 }
4019
4020 /*
4021  * basic testing of SMB2 ioctls
4022  */
4023 struct torture_suite *torture_smb2_ioctl_init(void)
4024 {
4025         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
4026
4027         torture_suite_add_1smb2_test(suite, "shadow_copy",
4028                                      test_ioctl_get_shadow_copy);
4029         torture_suite_add_1smb2_test(suite, "req_resume_key",
4030                                      test_ioctl_req_resume_key);
4031         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
4032                                      test_ioctl_copy_chunk_simple);
4033         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
4034                                      test_ioctl_copy_chunk_multi);
4035         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
4036                                      test_ioctl_copy_chunk_tiny);
4037         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
4038                                      test_ioctl_copy_chunk_over);
4039         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
4040                                      test_ioctl_copy_chunk_append);
4041         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
4042                                      test_ioctl_copy_chunk_limits);
4043         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
4044                                      test_ioctl_copy_chunk_src_lck);
4045         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
4046                                      test_ioctl_copy_chunk_dest_lck);
4047         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
4048                                      test_ioctl_copy_chunk_bad_key);
4049         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
4050                                      test_ioctl_copy_chunk_src_is_dest);
4051         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
4052                                      test_ioctl_copy_chunk_src_is_dest_overlap);
4053         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
4054                                      test_ioctl_copy_chunk_bad_access);
4055         torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
4056                                      test_ioctl_copy_chunk_write_access);
4057         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
4058                                      test_ioctl_copy_chunk_src_exceed);
4059         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
4060                                      test_ioctl_copy_chunk_src_exceed_multi);
4061         torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
4062                                      test_ioctl_copy_chunk_sparse_dest);
4063         torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
4064                                      test_ioctl_copy_chunk_max_output_sz);
4065         torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
4066                                      test_ioctl_copy_chunk_zero_length);
4067         torture_suite_add_1smb2_test(suite, "compress_file_flag",
4068                                      test_ioctl_compress_file_flag);
4069         torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
4070                                      test_ioctl_compress_dir_inherit);
4071         torture_suite_add_1smb2_test(suite, "compress_invalid_format",
4072                                      test_ioctl_compress_invalid_format);
4073         torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
4074                                      test_ioctl_compress_invalid_buf);
4075         torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
4076                                      test_ioctl_compress_query_file_attr);
4077         torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
4078                                      test_ioctl_compress_create_with_attr);
4079         torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
4080                                      test_ioctl_compress_inherit_disable);
4081         torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
4082                                      test_ioctl_compress_set_file_attr);
4083         torture_suite_add_1smb2_test(suite, "compress_perms",
4084                                      test_ioctl_compress_perms);
4085         torture_suite_add_1smb2_test(suite, "network_interface_info",
4086                                      test_ioctl_network_interface_info);
4087         torture_suite_add_1smb2_test(suite, "sparse_file_flag",
4088                                      test_ioctl_sparse_file_flag);
4089         torture_suite_add_1smb2_test(suite, "sparse_file_attr",
4090                                      test_ioctl_sparse_file_attr);
4091         torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
4092                                      test_ioctl_sparse_dir_flag);
4093         torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
4094                                      test_ioctl_sparse_set_nobuf);
4095         torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
4096                                      test_ioctl_sparse_set_oversize);
4097         torture_suite_add_1smb2_test(suite, "sparse_qar",
4098                                      test_ioctl_sparse_qar);
4099         torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
4100                                      test_ioctl_sparse_qar_malformed);
4101         torture_suite_add_1smb2_test(suite, "sparse_punch",
4102                                      test_ioctl_sparse_punch);
4103         torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",
4104                                      test_ioctl_sparse_hole_dealloc);
4105         torture_suite_add_1smb2_test(suite, "sparse_compressed",
4106                                      test_ioctl_sparse_compressed);
4107         torture_suite_add_1smb2_test(suite, "sparse_copy_chunk",
4108                                      test_ioctl_sparse_copy_chunk);
4109         torture_suite_add_1smb2_test(suite, "sparse_punch_invalid",
4110                                      test_ioctl_sparse_punch_invalid);
4111
4112         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
4113
4114         return suite;
4115 }
4116