torture: basic FSCTL_QUERY_ALLOCATED_RANGES test
[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-2013
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 buf_off = 0;
131
132         if (len == 0) {
133                 return true;
134         }
135
136         buf = talloc_zero_size(mem_ctx, len);
137         torture_assert(torture, (buf != NULL), "no memory for file data buf");
138
139         for (i = 0; i <= len - 8; i += 8) {
140                 SBVAL(buf, i, patt_hash(patt_off));
141                 patt_off += 8;
142         }
143
144         while (len > 0) {
145                 uint64_t io_sz = MIN(1024 * 1024, len);
146                 status = smb2_util_write(tree, h,
147                                          buf + buf_off, off, io_sz);
148                 torture_assert_ntstatus_ok(torture, status, "file write");
149
150                 len -= io_sz;
151                 buf_off += io_sz;
152                 off += io_sz;
153         }
154
155         return true;
156 }
157
158 static bool check_pattern(struct torture_context *torture,
159                           struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
160                           struct smb2_handle h, uint64_t off, uint64_t len,
161                           uint64_t patt_off)
162 {
163         uint64_t i;
164         struct smb2_read r;
165         NTSTATUS status;
166
167         ZERO_STRUCT(r);
168         r.in.file.handle = h;
169         r.in.length      = len;
170         r.in.offset      = off;
171         status = smb2_read(tree, mem_ctx, &r);
172         torture_assert_ntstatus_ok(torture, status, "read");
173
174         torture_assert_u64_equal(torture, r.out.data.length, len,
175                                  "read data len mismatch");
176
177         for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
178                 uint64_t data = BVAL(r.out.data.data, i);
179                 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
180                                          talloc_asprintf(torture, "read data "
181                                                          "pattern bad at %llu\n",
182                                                          (unsigned long long)i));
183         }
184
185         talloc_free(r.out.data.data);
186         return true;
187 }
188
189 static bool test_setup_open(struct torture_context *torture,
190                             struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
191                             const char *fname,
192                             struct smb2_handle *fh,
193                             uint32_t desired_access,
194                             uint32_t file_attributes)
195 {
196         struct smb2_create io;
197         NTSTATUS status;
198
199         ZERO_STRUCT(io);
200         io.in.desired_access = desired_access;
201         io.in.file_attributes = file_attributes;
202         io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
203         io.in.share_access =
204                 NTCREATEX_SHARE_ACCESS_DELETE|
205                 NTCREATEX_SHARE_ACCESS_READ|
206                 NTCREATEX_SHARE_ACCESS_WRITE;
207         if (file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
208                 io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
209         }
210         io.in.fname = fname;
211
212         status = smb2_create(tree, mem_ctx, &io);
213         torture_assert_ntstatus_ok(torture, status, "file create");
214
215         *fh = io.out.file.handle;
216
217         return true;
218 }
219
220 static bool test_setup_create_fill(struct torture_context *torture,
221                                    struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
222                                    const char *fname,
223                                    struct smb2_handle *fh,
224                                    uint64_t size,
225                                    uint32_t desired_access,
226                                    uint32_t file_attributes)
227 {
228         bool ok;
229
230         smb2_util_unlink(tree, fname);
231
232         ok = test_setup_open(torture, tree, mem_ctx,
233                              fname,
234                              fh,
235                              desired_access,
236                              file_attributes);
237         torture_assert(torture, ok, "file open");
238
239         if (size > 0) {
240                 ok = write_pattern(torture, tree, mem_ctx, *fh, 0, size, 0);
241                 torture_assert(torture, ok, "write pattern");
242         }
243         return true;
244 }
245
246 static bool test_setup_copy_chunk(struct torture_context *torture,
247                                   struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
248                                   uint32_t nchunks,
249                                   struct smb2_handle *src_h,
250                                   uint64_t src_size,
251                                   uint32_t src_desired_access,
252                                   struct smb2_handle *dest_h,
253                                   uint64_t dest_size,
254                                   uint32_t dest_desired_access,
255                                   struct srv_copychunk_copy *cc_copy,
256                                   union smb_ioctl *ioctl)
257 {
258         struct req_resume_key_rsp res_key;
259         bool ok;
260         NTSTATUS status;
261         enum ndr_err_code ndr_ret;
262
263         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
264                                     src_h, src_size, src_desired_access,
265                                     FILE_ATTRIBUTE_NORMAL);
266         torture_assert(torture, ok, "src file create fill");
267
268         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
269                                     dest_h, dest_size, dest_desired_access,
270                                     FILE_ATTRIBUTE_NORMAL);
271         torture_assert(torture, ok, "dest file create fill");
272
273         ZERO_STRUCTPN(ioctl);
274         ioctl->smb2.level = RAW_IOCTL_SMB2;
275         ioctl->smb2.in.file.handle = *src_h;
276         ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
277         /* Allow for Key + ContextLength + Context */
278         ioctl->smb2.in.max_response_size = 32;
279         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
280
281         status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
282         torture_assert_ntstatus_ok(torture, status,
283                                    "FSCTL_SRV_REQUEST_RESUME_KEY");
284
285         ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
286                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
287
288         torture_assert_ndr_success(torture, ndr_ret,
289                                    "ndr_pull_req_resume_key_rsp");
290
291         ZERO_STRUCTPN(ioctl);
292         ioctl->smb2.level = RAW_IOCTL_SMB2;
293         ioctl->smb2.in.file.handle = *dest_h;
294         ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
295         ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
296         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
297
298         ZERO_STRUCTPN(cc_copy);
299         memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
300         cc_copy->chunk_count = nchunks;
301         cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
302         torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
303
304         return true;
305 }
306
307
308 static bool check_copy_chunk_rsp(struct torture_context *torture,
309                                  struct srv_copychunk_rsp *cc_rsp,
310                                  uint32_t ex_chunks_written,
311                                  uint32_t ex_chunk_bytes_written,
312                                  uint32_t ex_total_bytes_written)
313 {
314         torture_assert_int_equal(torture, cc_rsp->chunks_written,
315                                  ex_chunks_written, "num chunks");
316         torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
317                                  ex_chunk_bytes_written, "chunk bytes written");
318         torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
319                                  ex_total_bytes_written, "chunk total bytes");
320         return true;
321 }
322
323 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
324                                          struct smb2_tree *tree)
325 {
326         struct smb2_handle src_h;
327         struct smb2_handle dest_h;
328         NTSTATUS status;
329         union smb_ioctl ioctl;
330         TALLOC_CTX *tmp_ctx = talloc_new(tree);
331         struct srv_copychunk_copy cc_copy;
332         struct srv_copychunk_rsp cc_rsp;
333         enum ndr_err_code ndr_ret;
334         bool ok;
335
336         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
337                                    1, /* 1 chunk */
338                                    &src_h, 4096, /* fill 4096 byte src file */
339                                    SEC_RIGHTS_FILE_ALL,
340                                    &dest_h, 0,  /* 0 byte dest file */
341                                    SEC_RIGHTS_FILE_ALL,
342                                    &cc_copy,
343                                    &ioctl);
344         if (!ok) {
345                 torture_fail(torture, "setup copy chunk error");
346         }
347
348         /* copy all src file data (via a single chunk desc) */
349         cc_copy.chunks[0].source_off = 0;
350         cc_copy.chunks[0].target_off = 0;
351         cc_copy.chunks[0].length = 4096;
352
353         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
354                                        &cc_copy,
355                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
356         torture_assert_ndr_success(torture, ndr_ret,
357                                    "ndr_push_srv_copychunk_copy");
358
359         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
360         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
361
362         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
363                                        &cc_rsp,
364                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
365         torture_assert_ndr_success(torture, ndr_ret,
366                                    "ndr_pull_srv_copychunk_rsp");
367
368         ok = check_copy_chunk_rsp(torture, &cc_rsp,
369                                   1,    /* chunks written */
370                                   0,    /* chunk bytes unsuccessfully written */
371                                   4096); /* total bytes written */
372         if (!ok) {
373                 torture_fail(torture, "bad copy chunk response data");
374         }
375
376         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
377         if (!ok) {
378                 torture_fail(torture, "inconsistent file data");
379         }
380
381         smb2_util_close(tree, src_h);
382         smb2_util_close(tree, dest_h);
383         talloc_free(tmp_ctx);
384         return true;
385 }
386
387 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
388                                         struct smb2_tree *tree)
389 {
390         struct smb2_handle src_h;
391         struct smb2_handle dest_h;
392         NTSTATUS status;
393         union smb_ioctl ioctl;
394         TALLOC_CTX *tmp_ctx = talloc_new(tree);
395         struct srv_copychunk_copy cc_copy;
396         struct srv_copychunk_rsp cc_rsp;
397         enum ndr_err_code ndr_ret;
398         bool ok;
399
400         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
401                                    2, /* chunks */
402                                    &src_h, 8192, /* src file */
403                                    SEC_RIGHTS_FILE_ALL,
404                                    &dest_h, 0,  /* dest file */
405                                    SEC_RIGHTS_FILE_ALL,
406                                    &cc_copy,
407                                    &ioctl);
408         if (!ok) {
409                 torture_fail(torture, "setup copy chunk error");
410         }
411
412         /* copy all src file data via two chunks */
413         cc_copy.chunks[0].source_off = 0;
414         cc_copy.chunks[0].target_off = 0;
415         cc_copy.chunks[0].length = 4096;
416
417         cc_copy.chunks[1].source_off = 4096;
418         cc_copy.chunks[1].target_off = 4096;
419         cc_copy.chunks[1].length = 4096;
420
421         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
422                                        &cc_copy,
423                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
424         torture_assert_ndr_success(torture, ndr_ret,
425                                    "ndr_push_srv_copychunk_copy");
426
427         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
428         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
429
430         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
431                                        &cc_rsp,
432                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
433         torture_assert_ndr_success(torture, ndr_ret,
434                                    "ndr_pull_srv_copychunk_rsp");
435
436         ok = check_copy_chunk_rsp(torture, &cc_rsp,
437                                   2,    /* chunks written */
438                                   0,    /* chunk bytes unsuccessfully written */
439                                   8192);        /* total bytes written */
440         if (!ok) {
441                 torture_fail(torture, "bad copy chunk response data");
442         }
443
444         smb2_util_close(tree, src_h);
445         smb2_util_close(tree, dest_h);
446         talloc_free(tmp_ctx);
447         return true;
448 }
449
450 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
451                                        struct smb2_tree *tree)
452 {
453         struct smb2_handle src_h;
454         struct smb2_handle dest_h;
455         NTSTATUS status;
456         union smb_ioctl ioctl;
457         TALLOC_CTX *tmp_ctx = talloc_new(tree);
458         struct srv_copychunk_copy cc_copy;
459         struct srv_copychunk_rsp cc_rsp;
460         enum ndr_err_code ndr_ret;
461         bool ok;
462
463         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
464                                    2, /* chunks */
465                                    &src_h, 100, /* src file */
466                                    SEC_RIGHTS_FILE_ALL,
467                                    &dest_h, 0,  /* dest file */
468                                    SEC_RIGHTS_FILE_ALL,
469                                    &cc_copy,
470                                    &ioctl);
471         if (!ok) {
472                 torture_fail(torture, "setup copy chunk error");
473         }
474
475         /* copy all src file data via two chunks, sub block size chunks */
476         cc_copy.chunks[0].source_off = 0;
477         cc_copy.chunks[0].target_off = 0;
478         cc_copy.chunks[0].length = 50;
479
480         cc_copy.chunks[1].source_off = 50;
481         cc_copy.chunks[1].target_off = 50;
482         cc_copy.chunks[1].length = 50;
483
484         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
485                                        &cc_copy,
486                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
487         torture_assert_ndr_success(torture, ndr_ret,
488                                    "ndr_push_srv_copychunk_copy");
489
490         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
491         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
492
493         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
494                                        &cc_rsp,
495                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
496         torture_assert_ndr_success(torture, ndr_ret,
497                                    "ndr_pull_srv_copychunk_rsp");
498
499         ok = check_copy_chunk_rsp(torture, &cc_rsp,
500                                   2,    /* chunks written */
501                                   0,    /* chunk bytes unsuccessfully written */
502                                   100); /* total bytes written */
503         if (!ok) {
504                 torture_fail(torture, "bad copy chunk response data");
505         }
506
507         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
508         if (!ok) {
509                 torture_fail(torture, "inconsistent file data");
510         }
511
512         smb2_util_close(tree, src_h);
513         smb2_util_close(tree, dest_h);
514         talloc_free(tmp_ctx);
515         return true;
516 }
517
518 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
519                                        struct smb2_tree *tree)
520 {
521         struct smb2_handle src_h;
522         struct smb2_handle dest_h;
523         NTSTATUS status;
524         union smb_ioctl ioctl;
525         TALLOC_CTX *tmp_ctx = talloc_new(tree);
526         struct srv_copychunk_copy cc_copy;
527         struct srv_copychunk_rsp cc_rsp;
528         enum ndr_err_code ndr_ret;
529         bool ok;
530
531         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
532                                    2, /* chunks */
533                                    &src_h, 8192, /* src file */
534                                    SEC_RIGHTS_FILE_ALL,
535                                    &dest_h, 4096, /* dest file */
536                                    SEC_RIGHTS_FILE_ALL,
537                                    &cc_copy,
538                                    &ioctl);
539         if (!ok) {
540                 torture_fail(torture, "setup copy chunk error");
541         }
542
543         /* first chunk overwrites existing dest data */
544         cc_copy.chunks[0].source_off = 0;
545         cc_copy.chunks[0].target_off = 0;
546         cc_copy.chunks[0].length = 4096;
547
548         /* second chunk overwrites the first */
549         cc_copy.chunks[1].source_off = 4096;
550         cc_copy.chunks[1].target_off = 0;
551         cc_copy.chunks[1].length = 4096;
552
553         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
554                                        &cc_copy,
555                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
556         torture_assert_ndr_success(torture, ndr_ret,
557                                    "ndr_push_srv_copychunk_copy");
558
559         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
560         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
561
562         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
563                                        &cc_rsp,
564                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
565         torture_assert_ndr_success(torture, ndr_ret,
566                                    "ndr_pull_srv_copychunk_rsp");
567
568         ok = check_copy_chunk_rsp(torture, &cc_rsp,
569                                   2,    /* chunks written */
570                                   0,    /* chunk bytes unsuccessfully written */
571                                   8192); /* total bytes written */
572         if (!ok) {
573                 torture_fail(torture, "bad copy chunk response data");
574         }
575
576         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
577         if (!ok) {
578                 torture_fail(torture, "inconsistent file data");
579         }
580
581         smb2_util_close(tree, src_h);
582         smb2_util_close(tree, dest_h);
583         talloc_free(tmp_ctx);
584         return true;
585 }
586
587 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
588                                        struct smb2_tree *tree)
589 {
590         struct smb2_handle src_h;
591         struct smb2_handle dest_h;
592         NTSTATUS status;
593         union smb_ioctl ioctl;
594         TALLOC_CTX *tmp_ctx = talloc_new(tree);
595         struct srv_copychunk_copy cc_copy;
596         struct srv_copychunk_rsp cc_rsp;
597         enum ndr_err_code ndr_ret;
598         bool ok;
599
600         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
601                                    2, /* chunks */
602                                    &src_h, 4096, /* src file */
603                                    SEC_RIGHTS_FILE_ALL,
604                                    &dest_h, 0,  /* dest file */
605                                    SEC_RIGHTS_FILE_ALL,
606                                    &cc_copy,
607                                    &ioctl);
608         if (!ok) {
609                 torture_fail(torture, "setup copy chunk error");
610         }
611
612         cc_copy.chunks[0].source_off = 0;
613         cc_copy.chunks[0].target_off = 0;
614         cc_copy.chunks[0].length = 4096;
615
616         /* second chunk appends the same data to the first */
617         cc_copy.chunks[1].source_off = 0;
618         cc_copy.chunks[1].target_off = 4096;
619         cc_copy.chunks[1].length = 4096;
620
621         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
622                                        &cc_copy,
623                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
624         torture_assert_ndr_success(torture, ndr_ret,
625                                    "ndr_push_srv_copychunk_copy");
626
627         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
628         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
629
630         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
631                                        &cc_rsp,
632                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
633         torture_assert_ndr_success(torture, ndr_ret,
634                                    "ndr_pull_srv_copychunk_rsp");
635
636         ok = check_copy_chunk_rsp(torture, &cc_rsp,
637                                   2,    /* chunks written */
638                                   0,    /* chunk bytes unsuccessfully written */
639                                   8192); /* total bytes written */
640         if (!ok) {
641                 torture_fail(torture, "bad copy chunk response data");
642         }
643
644         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
645         if (!ok) {
646                 torture_fail(torture, "inconsistent file data");
647         }
648
649         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
650         if (!ok) {
651                 torture_fail(torture, "inconsistent file data");
652         }
653
654         smb2_util_close(tree, src_h);
655         smb2_util_close(tree, dest_h);
656         talloc_free(tmp_ctx);
657         return true;
658 }
659
660 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
661                                          struct smb2_tree *tree)
662 {
663         struct smb2_handle src_h;
664         struct smb2_handle dest_h;
665         NTSTATUS status;
666         union smb_ioctl ioctl;
667         TALLOC_CTX *tmp_ctx = talloc_new(tree);
668         struct srv_copychunk_copy cc_copy;
669         struct srv_copychunk_rsp cc_rsp;
670         enum ndr_err_code ndr_ret;
671         bool ok;
672
673         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
674                                    1, /* chunks */
675                                    &src_h, 4096, /* src file */
676                                    SEC_RIGHTS_FILE_ALL,
677                                    &dest_h, 0,  /* dest file */
678                                    SEC_RIGHTS_FILE_ALL,
679                                    &cc_copy,
680                                    &ioctl);
681         if (!ok) {
682                 torture_fail(torture, "setup copy chunk error");
683         }
684
685         /* send huge chunk length request */
686         cc_copy.chunks[0].source_off = 0;
687         cc_copy.chunks[0].target_off = 0;
688         cc_copy.chunks[0].length = UINT_MAX;
689
690         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
691                                        &cc_copy,
692                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
693         torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
694
695         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
696         torture_assert_ntstatus_equal(torture, status,
697                                       NT_STATUS_INVALID_PARAMETER,
698                                       "bad oversize chunk response");
699
700         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
701                                        &cc_rsp,
702                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
703         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
704
705         torture_comment(torture, "limit max chunks, got %u\n",
706                         cc_rsp.chunks_written);
707         torture_comment(torture, "limit max chunk len, got %u\n",
708                         cc_rsp.chunk_bytes_written);
709         torture_comment(torture, "limit max total bytes, got %u\n",
710                         cc_rsp.total_bytes_written);
711
712         smb2_util_close(tree, src_h);
713         smb2_util_close(tree, dest_h);
714         talloc_free(tmp_ctx);
715         return true;
716 }
717
718 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
719                                           struct smb2_tree *tree)
720 {
721         struct smb2_handle src_h;
722         struct smb2_handle src_h2;
723         struct smb2_handle dest_h;
724         NTSTATUS status;
725         union smb_ioctl ioctl;
726         TALLOC_CTX *tmp_ctx = talloc_new(tree);
727         struct srv_copychunk_copy cc_copy;
728         struct srv_copychunk_rsp cc_rsp;
729         enum ndr_err_code ndr_ret;
730         bool ok;
731         struct smb2_lock lck;
732         struct smb2_lock_element el[1];
733
734         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
735                                    1, /* chunks */
736                                    &src_h, 4096, /* src file */
737                                    SEC_RIGHTS_FILE_ALL,
738                                    &dest_h, 0,  /* dest file */
739                                    SEC_RIGHTS_FILE_ALL,
740                                    &cc_copy,
741                                    &ioctl);
742         if (!ok) {
743                 torture_fail(torture, "setup copy chunk error");
744         }
745
746         cc_copy.chunks[0].source_off = 0;
747         cc_copy.chunks[0].target_off = 0;
748         cc_copy.chunks[0].length = 4096;
749
750         /* open and lock the copychunk src file */
751         status = torture_smb2_testfile(tree, FNAME, &src_h2);
752         torture_assert_ntstatus_ok(torture, status, "2nd src open");
753
754         lck.in.lock_count       = 0x0001;
755         lck.in.lock_sequence    = 0x00000000;
756         lck.in.file.handle      = src_h2;
757         lck.in.locks            = el;
758         el[0].offset            = cc_copy.chunks[0].source_off;
759         el[0].length            = cc_copy.chunks[0].length;
760         el[0].reserved          = 0;
761         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
762
763         status = smb2_lock(tree, &lck);
764         torture_assert_ntstatus_ok(torture, status, "lock");
765
766         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
767                                        &cc_copy,
768                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
769         torture_assert_ndr_success(torture, ndr_ret,
770                                    "ndr_push_srv_copychunk_copy");
771
772         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
773         /*
774          * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
775          *
776          * Edgar Olougouna @ MS wrote:
777          * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
778          * discrepancy observed between Windows versions, we confirm that the
779          * behavior change is expected.
780          *
781          * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
782          * to move the chunks from the source to the destination.
783          * These ReadFile/WriteFile APIs go through the byte-range lock checks,
784          * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
785          *
786          * Prior to Windows Server 2012, CopyChunk used mapped sections to move
787          * the data. And byte range locks are not enforced on mapped I/O, and
788          * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
789          */
790         torture_assert_ntstatus_equal(torture, status,
791                                       NT_STATUS_FILE_LOCK_CONFLICT,
792                                       "FSCTL_SRV_COPYCHUNK locked");
793
794         /* should get cc response data with the lock conflict status */
795         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
796                                        &cc_rsp,
797                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
798         torture_assert_ndr_success(torture, ndr_ret,
799                                    "ndr_pull_srv_copychunk_rsp");
800         ok = check_copy_chunk_rsp(torture, &cc_rsp,
801                                   0,    /* chunks written */
802                                   0,    /* chunk bytes unsuccessfully written */
803                                   0);   /* total bytes written */
804
805         lck.in.lock_count       = 0x0001;
806         lck.in.lock_sequence    = 0x00000001;
807         lck.in.file.handle      = src_h2;
808         lck.in.locks            = el;
809         el[0].offset            = cc_copy.chunks[0].source_off;
810         el[0].length            = cc_copy.chunks[0].length;
811         el[0].reserved          = 0;
812         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
813         status = smb2_lock(tree, &lck);
814         torture_assert_ntstatus_ok(torture, status, "unlock");
815
816         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
817         torture_assert_ntstatus_ok(torture, status,
818                                    "FSCTL_SRV_COPYCHUNK unlocked");
819
820         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
821                                        &cc_rsp,
822                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
823         torture_assert_ndr_success(torture, ndr_ret,
824                                    "ndr_pull_srv_copychunk_rsp");
825
826         ok = check_copy_chunk_rsp(torture, &cc_rsp,
827                                   1,    /* chunks written */
828                                   0,    /* chunk bytes unsuccessfully written */
829                                   4096); /* total bytes written */
830         if (!ok) {
831                 torture_fail(torture, "bad copy chunk response data");
832         }
833
834         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
835         if (!ok) {
836                 torture_fail(torture, "inconsistent file data");
837         }
838
839         smb2_util_close(tree, src_h2);
840         smb2_util_close(tree, src_h);
841         smb2_util_close(tree, dest_h);
842         talloc_free(tmp_ctx);
843         return true;
844 }
845
846 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
847                                            struct smb2_tree *tree)
848 {
849         struct smb2_handle src_h;
850         struct smb2_handle dest_h;
851         struct smb2_handle dest_h2;
852         NTSTATUS status;
853         union smb_ioctl ioctl;
854         TALLOC_CTX *tmp_ctx = talloc_new(tree);
855         struct srv_copychunk_copy cc_copy;
856         struct srv_copychunk_rsp cc_rsp;
857         enum ndr_err_code ndr_ret;
858         bool ok;
859         struct smb2_lock lck;
860         struct smb2_lock_element el[1];
861
862         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
863                                    1, /* chunks */
864                                    &src_h, 4096, /* src file */
865                                    SEC_RIGHTS_FILE_ALL,
866                                    &dest_h, 4096,       /* dest file */
867                                    SEC_RIGHTS_FILE_ALL,
868                                    &cc_copy,
869                                    &ioctl);
870         if (!ok) {
871                 torture_fail(torture, "setup copy chunk error");
872         }
873
874         cc_copy.chunks[0].source_off = 0;
875         cc_copy.chunks[0].target_off = 0;
876         cc_copy.chunks[0].length = 4096;
877
878         /* open and lock the copychunk dest file */
879         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
880         torture_assert_ntstatus_ok(torture, status, "2nd src open");
881
882         lck.in.lock_count       = 0x0001;
883         lck.in.lock_sequence    = 0x00000000;
884         lck.in.file.handle      = dest_h2;
885         lck.in.locks            = el;
886         el[0].offset            = cc_copy.chunks[0].target_off;
887         el[0].length            = cc_copy.chunks[0].length;
888         el[0].reserved          = 0;
889         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
890
891         status = smb2_lock(tree, &lck);
892         torture_assert_ntstatus_ok(torture, status, "lock");
893
894         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
895                                        &cc_copy,
896                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
897         torture_assert_ndr_success(torture, ndr_ret,
898                                    "ndr_push_srv_copychunk_copy");
899
900         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
901         torture_assert_ntstatus_equal(torture, status,
902                                       NT_STATUS_FILE_LOCK_CONFLICT,
903                                       "FSCTL_SRV_COPYCHUNK locked");
904
905         lck.in.lock_count       = 0x0001;
906         lck.in.lock_sequence    = 0x00000001;
907         lck.in.file.handle      = dest_h2;
908         lck.in.locks            = el;
909         el[0].offset            = cc_copy.chunks[0].target_off;
910         el[0].length            = cc_copy.chunks[0].length;
911         el[0].reserved          = 0;
912         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
913         status = smb2_lock(tree, &lck);
914         torture_assert_ntstatus_ok(torture, status, "unlock");
915
916         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
917         torture_assert_ntstatus_ok(torture, status,
918                                    "FSCTL_SRV_COPYCHUNK unlocked");
919
920         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
921                                        &cc_rsp,
922                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
923         torture_assert_ndr_success(torture, ndr_ret,
924                                    "ndr_pull_srv_copychunk_rsp");
925
926         ok = check_copy_chunk_rsp(torture, &cc_rsp,
927                                   1,    /* chunks written */
928                                   0,    /* chunk bytes unsuccessfully written */
929                                   4096); /* total bytes written */
930         if (!ok) {
931                 torture_fail(torture, "bad copy chunk response data");
932         }
933
934         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
935         if (!ok) {
936                 torture_fail(torture, "inconsistent file data");
937         }
938
939         smb2_util_close(tree, dest_h2);
940         smb2_util_close(tree, src_h);
941         smb2_util_close(tree, dest_h);
942         talloc_free(tmp_ctx);
943         return true;
944 }
945
946 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
947                                           struct smb2_tree *tree)
948 {
949         struct smb2_handle src_h;
950         struct smb2_handle dest_h;
951         NTSTATUS status;
952         union smb_ioctl ioctl;
953         TALLOC_CTX *tmp_ctx = talloc_new(tree);
954         struct srv_copychunk_copy cc_copy;
955         enum ndr_err_code ndr_ret;
956         bool ok;
957
958         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
959                                    1,
960                                    &src_h, 4096,
961                                    SEC_RIGHTS_FILE_ALL,
962                                    &dest_h, 0,
963                                    SEC_RIGHTS_FILE_ALL,
964                                    &cc_copy,
965                                    &ioctl);
966         if (!ok) {
967                 torture_fail(torture, "setup copy chunk error");
968         }
969
970         /* overwrite the resume key with a bogus value */
971         memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
972
973         cc_copy.chunks[0].source_off = 0;
974         cc_copy.chunks[0].target_off = 0;
975         cc_copy.chunks[0].length = 4096;
976
977         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
978                                        &cc_copy,
979                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
980         torture_assert_ndr_success(torture, ndr_ret,
981                                    "ndr_push_srv_copychunk_copy");
982
983         /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
984         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
985         torture_assert_ntstatus_equal(torture, status,
986                                       NT_STATUS_OBJECT_NAME_NOT_FOUND,
987                                       "FSCTL_SRV_COPYCHUNK");
988
989         smb2_util_close(tree, src_h);
990         smb2_util_close(tree, dest_h);
991         talloc_free(tmp_ctx);
992         return true;
993 }
994
995 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
996                                               struct smb2_tree *tree)
997 {
998         struct smb2_handle src_h;
999         struct smb2_handle dest_h;
1000         NTSTATUS status;
1001         union smb_ioctl ioctl;
1002         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1003         struct srv_copychunk_copy cc_copy;
1004         struct srv_copychunk_rsp cc_rsp;
1005         enum ndr_err_code ndr_ret;
1006         bool ok;
1007
1008         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1009                                    1,
1010                                    &src_h, 8192,
1011                                    SEC_RIGHTS_FILE_ALL,
1012                                    &dest_h, 0,
1013                                    SEC_RIGHTS_FILE_ALL,
1014                                    &cc_copy,
1015                                    &ioctl);
1016         if (!ok) {
1017                 torture_fail(torture, "setup copy chunk error");
1018         }
1019
1020         /* the source is also the destination */
1021         ioctl.smb2.in.file.handle = src_h;
1022
1023         /* non-overlapping */
1024         cc_copy.chunks[0].source_off = 0;
1025         cc_copy.chunks[0].target_off = 4096;
1026         cc_copy.chunks[0].length = 4096;
1027
1028         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1029                                        &cc_copy,
1030                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1031         torture_assert_ndr_success(torture, ndr_ret,
1032                                    "ndr_push_srv_copychunk_copy");
1033
1034         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1035         torture_assert_ntstatus_ok(torture, status,
1036                                    "FSCTL_SRV_COPYCHUNK");
1037
1038         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1039                                        &cc_rsp,
1040                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1041         torture_assert_ndr_success(torture, ndr_ret,
1042                                    "ndr_pull_srv_copychunk_rsp");
1043
1044         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1045                                   1,    /* chunks written */
1046                                   0,    /* chunk bytes unsuccessfully written */
1047                                   4096); /* total bytes written */
1048         if (!ok) {
1049                 torture_fail(torture, "bad copy chunk response data");
1050         }
1051
1052         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1053         if (!ok) {
1054                 torture_fail(torture, "inconsistent file data");
1055         }
1056         ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1057         if (!ok) {
1058                 torture_fail(torture, "inconsistent file data");
1059         }
1060
1061         smb2_util_close(tree, src_h);
1062         smb2_util_close(tree, dest_h);
1063         talloc_free(tmp_ctx);
1064         return true;
1065 }
1066
1067 /*
1068  * Test a single-chunk copychunk request, where the source and target ranges
1069  * overlap, and the SourceKey refers to the same target file. E.g:
1070  *
1071  * Initial State
1072  * -------------
1073  *      File:           src_and_dest
1074  *      Offset:         0123456789
1075  *      Data:           abcdefghij
1076  *
1077  * Request
1078  * -------
1079  *      FSCTL_SRV_COPYCHUNK(src_and_dest)
1080  *      SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1081  *      ChunkCount = 1
1082  *      Chunks[0].SourceOffset = 0
1083  *      Chunks[0].TargetOffset = 4
1084  *      Chunks[0].Length = 6
1085  *
1086  * Resultant State
1087  * ---------------
1088  *      File:           src_and_dest
1089  *      Offset:         0123456789
1090  *      Data:           abcdabcdef
1091  *
1092  * The resultant contents of src_and_dest is dependent on the server's
1093  * copy algorithm. In the above example, the server uses an IO buffer
1094  * large enough to hold the entire six-byte source data before writing
1095  * to TargetOffset. If the server were to use a four-byte IO buffer and
1096  * started reads/writes from the lowest offset, then the two overlapping
1097  * bytes in the above example would be overwritten before being read. The
1098  * resultant file contents would be abcdabcdab.
1099  *
1100  * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1101  * after this offset are written before being read. Windows 2012 on the
1102  * other hand appears to use a buffer large enough to hold its maximum
1103  * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1104  * default (vfs_cc_state.buf).
1105  *
1106  * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1107  * Windows 2008r2, 2012 and Samba servers. Note, 2008GM fails, as it appears
1108  * to use a different copy algorithm to 2008r2.
1109  */
1110 static bool
1111 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1112                                           struct smb2_tree *tree)
1113 {
1114         struct smb2_handle src_h;
1115         struct smb2_handle dest_h;
1116         NTSTATUS status;
1117         union smb_ioctl ioctl;
1118         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1119         struct srv_copychunk_copy cc_copy;
1120         struct srv_copychunk_rsp cc_rsp;
1121         enum ndr_err_code ndr_ret;
1122         bool ok;
1123
1124         /* exceed the vfs_default copy buffer */
1125         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1126                                    1,
1127                                    &src_h, 2048 * 2,
1128                                    SEC_RIGHTS_FILE_ALL,
1129                                    &dest_h, 0,
1130                                    SEC_RIGHTS_FILE_ALL,
1131                                    &cc_copy,
1132                                    &ioctl);
1133         if (!ok) {
1134                 torture_fail(torture, "setup copy chunk error");
1135         }
1136
1137         /* the source is also the destination */
1138         ioctl.smb2.in.file.handle = src_h;
1139
1140         /* 8 bytes overlap between source and target ranges */
1141         cc_copy.chunks[0].source_off = 0;
1142         cc_copy.chunks[0].target_off = 2048 - 8;
1143         cc_copy.chunks[0].length = 2048;
1144
1145         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1146                                        &cc_copy,
1147                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1148         torture_assert_ndr_success(torture, ndr_ret,
1149                                    "ndr_push_srv_copychunk_copy");
1150
1151         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1152         torture_assert_ntstatus_ok(torture, status,
1153                                    "FSCTL_SRV_COPYCHUNK");
1154
1155         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1156                                        &cc_rsp,
1157                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1158         torture_assert_ndr_success(torture, ndr_ret,
1159                                    "ndr_pull_srv_copychunk_rsp");
1160
1161         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1162                                   1,    /* chunks written */
1163                                   0,    /* chunk bytes unsuccessfully written */
1164                                   2048); /* total bytes written */
1165         if (!ok) {
1166                 torture_fail(torture, "bad copy chunk response data");
1167         }
1168
1169         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1170         if (!ok) {
1171                 torture_fail(torture, "inconsistent file data");
1172         }
1173         ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1174         if (!ok) {
1175                 torture_fail(torture, "inconsistent file data");
1176         }
1177
1178         smb2_util_close(tree, src_h);
1179         smb2_util_close(tree, dest_h);
1180         talloc_free(tmp_ctx);
1181         return true;
1182 }
1183
1184 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1185                                              struct smb2_tree *tree)
1186 {
1187         struct smb2_handle src_h;
1188         struct smb2_handle dest_h;
1189         NTSTATUS status;
1190         union smb_ioctl ioctl;
1191         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1192         struct srv_copychunk_copy cc_copy;
1193         enum ndr_err_code ndr_ret;
1194         bool ok;
1195
1196         /* no read permission on src */
1197         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1198                                    1, /* 1 chunk */
1199                                    &src_h, 4096, /* fill 4096 byte src file */
1200                                    SEC_RIGHTS_FILE_WRITE,
1201                                    &dest_h, 0,  /* 0 byte dest file */
1202                                    SEC_RIGHTS_FILE_ALL,
1203                                    &cc_copy,
1204                                    &ioctl);
1205         if (!ok) {
1206                 torture_fail(torture, "setup copy chunk error");
1207         }
1208
1209         cc_copy.chunks[0].source_off = 0;
1210         cc_copy.chunks[0].target_off = 0;
1211         cc_copy.chunks[0].length = 4096;
1212
1213         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1214                                        &cc_copy,
1215                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1216         torture_assert_ndr_success(torture, ndr_ret,
1217                                    "ndr_push_srv_copychunk_copy");
1218
1219         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1220         torture_assert_ntstatus_equal(torture, status,
1221                                       NT_STATUS_ACCESS_DENIED,
1222                                       "FSCTL_SRV_COPYCHUNK");
1223
1224         smb2_util_close(tree, src_h);
1225         smb2_util_close(tree, dest_h);
1226
1227         /* no write permission on dest */
1228         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1229                                    1, /* 1 chunk */
1230                                    &src_h, 4096, /* fill 4096 byte src file */
1231                                    SEC_RIGHTS_FILE_ALL,
1232                                    &dest_h, 0,  /* 0 byte dest file */
1233                                    (SEC_RIGHTS_FILE_READ
1234                                     | SEC_RIGHTS_FILE_EXECUTE),
1235                                    &cc_copy,
1236                                    &ioctl);
1237         if (!ok) {
1238                 torture_fail(torture, "setup copy chunk error");
1239         }
1240
1241         cc_copy.chunks[0].source_off = 0;
1242         cc_copy.chunks[0].target_off = 0;
1243         cc_copy.chunks[0].length = 4096;
1244
1245         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1246                                        &cc_copy,
1247                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1248         torture_assert_ndr_success(torture, ndr_ret,
1249                                    "ndr_push_srv_copychunk_copy");
1250
1251         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1252         torture_assert_ntstatus_equal(torture, status,
1253                                       NT_STATUS_ACCESS_DENIED,
1254                                       "FSCTL_SRV_COPYCHUNK");
1255
1256         smb2_util_close(tree, src_h);
1257         smb2_util_close(tree, dest_h);
1258
1259         /* no read permission on dest */
1260         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1261                                    1, /* 1 chunk */
1262                                    &src_h, 4096, /* fill 4096 byte src file */
1263                                    SEC_RIGHTS_FILE_ALL,
1264                                    &dest_h, 0,  /* 0 byte dest file */
1265                                    (SEC_RIGHTS_FILE_WRITE
1266                                     | SEC_RIGHTS_FILE_EXECUTE),
1267                                    &cc_copy,
1268                                    &ioctl);
1269         if (!ok) {
1270                 torture_fail(torture, "setup copy chunk error");
1271         }
1272
1273         cc_copy.chunks[0].source_off = 0;
1274         cc_copy.chunks[0].target_off = 0;
1275         cc_copy.chunks[0].length = 4096;
1276
1277         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1278                                        &cc_copy,
1279                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1280         torture_assert_ndr_success(torture, ndr_ret,
1281                                    "ndr_push_srv_copychunk_copy");
1282
1283         /*
1284          * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1285          * FSCTL_SRV_COPYCHUNK_WRITE on the other hand does not.
1286          */
1287         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1288         torture_assert_ntstatus_equal(torture, status,
1289                                       NT_STATUS_ACCESS_DENIED,
1290                                       "FSCTL_SRV_COPYCHUNK");
1291
1292         smb2_util_close(tree, src_h);
1293         smb2_util_close(tree, dest_h);
1294         talloc_free(tmp_ctx);
1295
1296         return true;
1297 }
1298
1299 static bool test_ioctl_copy_chunk_write_access(struct torture_context *torture,
1300                                                struct smb2_tree *tree)
1301 {
1302         struct smb2_handle src_h;
1303         struct smb2_handle dest_h;
1304         NTSTATUS status;
1305         union smb_ioctl ioctl;
1306         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1307         struct srv_copychunk_copy cc_copy;
1308         enum ndr_err_code ndr_ret;
1309         bool ok;
1310
1311         /* no read permission on dest with FSCTL_SRV_COPYCHUNK_WRITE */
1312         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1313                                    1, /* 1 chunk */
1314                                    &src_h, 4096, /* fill 4096 byte src file */
1315                                    SEC_RIGHTS_FILE_ALL,
1316                                    &dest_h, 0,  /* 0 byte dest file */
1317                                    (SEC_RIGHTS_FILE_WRITE
1318                                     | SEC_RIGHTS_FILE_EXECUTE),
1319                                    &cc_copy,
1320                                    &ioctl);
1321         if (!ok) {
1322                 torture_fail(torture, "setup copy chunk error");
1323         }
1324
1325         ioctl.smb2.in.function = FSCTL_SRV_COPYCHUNK_WRITE;
1326         cc_copy.chunks[0].source_off = 0;
1327         cc_copy.chunks[0].target_off = 0;
1328         cc_copy.chunks[0].length = 4096;
1329
1330         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1331                                        &cc_copy,
1332                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1333         torture_assert_ndr_success(torture, ndr_ret,
1334                                    "ndr_push_srv_copychunk_copy");
1335
1336         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1337         torture_assert_ntstatus_ok(torture, status,
1338                                    "FSCTL_SRV_COPYCHUNK_WRITE");
1339
1340         smb2_util_close(tree, src_h);
1341         smb2_util_close(tree, dest_h);
1342         talloc_free(tmp_ctx);
1343
1344         return true;
1345 }
1346
1347 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1348                                              struct smb2_tree *tree)
1349 {
1350         struct smb2_handle src_h;
1351         struct smb2_handle dest_h;
1352         NTSTATUS status;
1353         union smb_ioctl ioctl;
1354         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1355         struct srv_copychunk_copy cc_copy;
1356         struct srv_copychunk_rsp cc_rsp;
1357         enum ndr_err_code ndr_ret;
1358         bool ok;
1359
1360         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1361                                    1, /* 1 chunk */
1362                                    &src_h, 4096, /* fill 4096 byte src file */
1363                                    SEC_RIGHTS_FILE_ALL,
1364                                    &dest_h, 0,  /* 0 byte dest file */
1365                                    SEC_RIGHTS_FILE_ALL,
1366                                    &cc_copy,
1367                                    &ioctl);
1368         if (!ok) {
1369                 torture_fail(torture, "setup copy chunk error");
1370         }
1371
1372         /* Request copy where off + length exceeds size of src */
1373         cc_copy.chunks[0].source_off = 1024;
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_equal(torture, status,
1385                                       NT_STATUS_INVALID_VIEW_SIZE,
1386                                       "FSCTL_SRV_COPYCHUNK oversize");
1387
1388         /* Request copy where length exceeds size of src */
1389         cc_copy.chunks[0].source_off = 1024;
1390         cc_copy.chunks[0].target_off = 0;
1391         cc_copy.chunks[0].length = 3072;
1392
1393         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1394                                        &cc_copy,
1395                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1396         torture_assert_ndr_success(torture, ndr_ret,
1397                                    "ndr_push_srv_copychunk_copy");
1398
1399         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1400         torture_assert_ntstatus_ok(torture, status,
1401                                    "FSCTL_SRV_COPYCHUNK just right");
1402
1403         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1404                                        &cc_rsp,
1405                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1406         torture_assert_ndr_success(torture, ndr_ret,
1407                                    "ndr_pull_srv_copychunk_rsp");
1408
1409         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1410                                   1,    /* chunks written */
1411                                   0,    /* chunk bytes unsuccessfully written */
1412                                   3072); /* total bytes written */
1413         if (!ok) {
1414                 torture_fail(torture, "bad copy chunk response data");
1415         }
1416
1417         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1418         if (!ok) {
1419                 torture_fail(torture, "inconsistent file data");
1420         }
1421
1422         smb2_util_close(tree, src_h);
1423         smb2_util_close(tree, dest_h);
1424         talloc_free(tmp_ctx);
1425         return true;
1426 }
1427
1428 static bool
1429 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1430                                        struct smb2_tree *tree)
1431 {
1432         struct smb2_handle src_h;
1433         struct smb2_handle dest_h;
1434         NTSTATUS status;
1435         union smb_ioctl ioctl;
1436         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1437         struct srv_copychunk_copy cc_copy;
1438         struct srv_copychunk_rsp cc_rsp;
1439         enum ndr_err_code ndr_ret;
1440         bool ok;
1441
1442         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1443                                    2, /* 2 chunks */
1444                                    &src_h, 8192, /* fill 8192 byte src file */
1445                                    SEC_RIGHTS_FILE_ALL,
1446                                    &dest_h, 0,  /* 0 byte dest file */
1447                                    SEC_RIGHTS_FILE_ALL,
1448                                    &cc_copy,
1449                                    &ioctl);
1450         if (!ok) {
1451                 torture_fail(torture, "setup copy chunk error");
1452         }
1453
1454         /* Request copy where off + length exceeds size of src */
1455         cc_copy.chunks[0].source_off = 0;
1456         cc_copy.chunks[0].target_off = 0;
1457         cc_copy.chunks[0].length = 4096;
1458
1459         cc_copy.chunks[1].source_off = 4096;
1460         cc_copy.chunks[1].target_off = 4096;
1461         cc_copy.chunks[1].length = 8192;
1462
1463         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1464                                        &cc_copy,
1465                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1466         torture_assert_ndr_success(torture, ndr_ret,
1467                                    "ndr_push_srv_copychunk_copy");
1468
1469         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1470         torture_assert_ntstatus_equal(torture, status,
1471                                       NT_STATUS_INVALID_VIEW_SIZE,
1472                                       "FSCTL_SRV_COPYCHUNK oversize");
1473         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1474                                        &cc_rsp,
1475                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1476         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1477
1478         /* first chunk should still be written */
1479         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1480                                   1,    /* chunks written */
1481                                   0,    /* chunk bytes unsuccessfully written */
1482                                   4096); /* total bytes written */
1483         if (!ok) {
1484                 torture_fail(torture, "bad copy chunk response data");
1485         }
1486         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1487         if (!ok) {
1488                 torture_fail(torture, "inconsistent file data");
1489         }
1490
1491         smb2_util_close(tree, src_h);
1492         smb2_util_close(tree, dest_h);
1493         talloc_free(tmp_ctx);
1494         return true;
1495 }
1496
1497 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1498                                               struct smb2_tree *tree)
1499 {
1500         struct smb2_handle src_h;
1501         struct smb2_handle dest_h;
1502         NTSTATUS status;
1503         union smb_ioctl ioctl;
1504         struct smb2_read r;
1505         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1506         struct srv_copychunk_copy cc_copy;
1507         struct srv_copychunk_rsp cc_rsp;
1508         enum ndr_err_code ndr_ret;
1509         bool ok;
1510         int i;
1511
1512         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1513                                    1, /* 1 chunk */
1514                                    &src_h, 4096, /* fill 4096 byte src file */
1515                                    SEC_RIGHTS_FILE_ALL,
1516                                    &dest_h, 0,  /* 0 byte dest file */
1517                                    SEC_RIGHTS_FILE_ALL,
1518                                    &cc_copy,
1519                                    &ioctl);
1520         if (!ok) {
1521                 torture_fail(torture, "setup copy chunk error");
1522         }
1523
1524         /* copy all src file data (via a single chunk desc) */
1525         cc_copy.chunks[0].source_off = 0;
1526         cc_copy.chunks[0].target_off = 4096;
1527         cc_copy.chunks[0].length = 4096;
1528
1529         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1530                                        &cc_copy,
1531                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1532         torture_assert_ndr_success(torture, ndr_ret,
1533                                    "ndr_push_srv_copychunk_copy");
1534
1535         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1536         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1537
1538         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1539                                        &cc_rsp,
1540                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1541         torture_assert_ndr_success(torture, ndr_ret,
1542                                    "ndr_pull_srv_copychunk_rsp");
1543
1544         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1545                                   1,    /* chunks written */
1546                                   0,    /* chunk bytes unsuccessfully written */
1547                                   4096); /* total bytes written */
1548         if (!ok) {
1549                 torture_fail(torture, "bad copy chunk response data");
1550         }
1551
1552         /* check for zeros in first 4k */
1553         ZERO_STRUCT(r);
1554         r.in.file.handle = dest_h;
1555         r.in.length      = 4096;
1556         r.in.offset      = 0;
1557         status = smb2_read(tree, tmp_ctx, &r);
1558         torture_assert_ntstatus_ok(torture, status, "read");
1559
1560         torture_assert_u64_equal(torture, r.out.data.length, 4096,
1561                                  "read data len mismatch");
1562
1563         for (i = 0; i < 4096; i++) {
1564                 torture_assert(torture, (r.out.data.data[i] == 0),
1565                                "sparse did not pass class");
1566         }
1567
1568         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1569         if (!ok) {
1570                 torture_fail(torture, "inconsistent file data");
1571         }
1572
1573         smb2_util_close(tree, src_h);
1574         smb2_util_close(tree, dest_h);
1575         talloc_free(tmp_ctx);
1576         return true;
1577 }
1578
1579 /*
1580  * set the ioctl MaxOutputResponse size to less than
1581  * sizeof(struct srv_copychunk_rsp)
1582  */
1583 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1584                                                 struct smb2_tree *tree)
1585 {
1586         struct smb2_handle src_h;
1587         struct smb2_handle dest_h;
1588         NTSTATUS status;
1589         union smb_ioctl ioctl;
1590         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1591         struct srv_copychunk_copy cc_copy;
1592         enum ndr_err_code ndr_ret;
1593         bool ok;
1594
1595         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1596                                    1, /* 1 chunk */
1597                                    &src_h, 4096, /* fill 4096 byte src file */
1598                                    SEC_RIGHTS_FILE_ALL,
1599                                    &dest_h, 0,  /* 0 byte dest file */
1600                                    SEC_RIGHTS_FILE_ALL,
1601                                    &cc_copy,
1602                                    &ioctl);
1603         if (!ok) {
1604                 torture_fail(torture, "setup copy chunk error");
1605         }
1606
1607         cc_copy.chunks[0].source_off = 0;
1608         cc_copy.chunks[0].target_off = 0;
1609         cc_copy.chunks[0].length = 4096;
1610         /* req is valid, but use undersize max_response_size */
1611         ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1612
1613         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1614                                        &cc_copy,
1615                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1616         torture_assert_ndr_success(torture, ndr_ret,
1617                                    "ndr_push_srv_copychunk_copy");
1618
1619         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1620         torture_assert_ntstatus_equal(torture, status,
1621                                       NT_STATUS_INVALID_PARAMETER,
1622                                       "FSCTL_SRV_COPYCHUNK");
1623
1624         smb2_util_close(tree, src_h);
1625         smb2_util_close(tree, dest_h);
1626         talloc_free(tmp_ctx);
1627         return true;
1628 }
1629
1630 static bool test_ioctl_copy_chunk_zero_length(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         union smb_fileinfo q;
1638         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1639         struct srv_copychunk_copy cc_copy;
1640         struct srv_copychunk_rsp cc_rsp;
1641         enum ndr_err_code ndr_ret;
1642         bool ok;
1643
1644         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1645                                    1, /* 1 chunk */
1646                                    &src_h, 4096, /* fill 4096 byte src file */
1647                                    SEC_RIGHTS_FILE_ALL,
1648                                    &dest_h, 0,  /* 0 byte dest file */
1649                                    SEC_RIGHTS_FILE_ALL,
1650                                    &cc_copy,
1651                                    &ioctl);
1652         if (!ok) {
1653                 torture_fail(torture, "setup copy chunk error");
1654         }
1655
1656         /* zero length server-side copy (via a single chunk desc) */
1657         cc_copy.chunks[0].source_off = 0;
1658         cc_copy.chunks[0].target_off = 0;
1659         cc_copy.chunks[0].length = 0;
1660
1661         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1662                                        &cc_copy,
1663                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1664         torture_assert_ndr_success(torture, ndr_ret,
1665                                    "ndr_push_srv_copychunk_copy");
1666
1667         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1668         torture_assert_ntstatus_equal(torture, status,
1669                                       NT_STATUS_INVALID_PARAMETER,
1670                                       "bad zero-length chunk response");
1671
1672         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1673                                        &cc_rsp,
1674                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1675         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1676
1677         ZERO_STRUCT(q);
1678         q.all_info2.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1679         q.all_info2.in.file.handle = dest_h;
1680         status = smb2_getinfo_file(tree, torture, &q);
1681         torture_assert_ntstatus_ok(torture, status, "getinfo");
1682
1683         torture_assert_int_equal(torture, q.all_info2.out.size, 0,
1684                                  "size after zero len clone");
1685
1686         smb2_util_close(tree, src_h);
1687         smb2_util_close(tree, dest_h);
1688         talloc_free(tmp_ctx);
1689         return true;
1690 }
1691
1692 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1693                                                  struct smb2_tree *tree,
1694                                                  TALLOC_CTX *mem_ctx,
1695                                                  struct smb2_handle *fh,
1696                                                  bool *compress_support)
1697 {
1698         NTSTATUS status;
1699         union smb_fsinfo info;
1700
1701         ZERO_STRUCT(info);
1702         info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1703         info.generic.handle = *fh;
1704         status = smb2_getinfo_fs(tree, tree, &info);
1705         if (!NT_STATUS_IS_OK(status)) {
1706                 return status;
1707         }
1708
1709         if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1710                 *compress_support = true;
1711         } else {
1712                 *compress_support = false;
1713         }
1714         return NT_STATUS_OK;
1715 }
1716
1717 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1718                                         TALLOC_CTX *mem_ctx,
1719                                         struct smb2_tree *tree,
1720                                         struct smb2_handle fh,
1721                                         uint16_t *_compression_fmt)
1722 {
1723         union smb_ioctl ioctl;
1724         struct compression_state cmpr_state;
1725         enum ndr_err_code ndr_ret;
1726         NTSTATUS status;
1727
1728         ZERO_STRUCT(ioctl);
1729         ioctl.smb2.level = RAW_IOCTL_SMB2;
1730         ioctl.smb2.in.file.handle = fh;
1731         ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1732         ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1733         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1734
1735         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1736         if (!NT_STATUS_IS_OK(status)) {
1737                 return status;
1738         }
1739
1740         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1741                                        &cmpr_state,
1742                         (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1743
1744         if (ndr_ret != NDR_ERR_SUCCESS) {
1745                 return NT_STATUS_INTERNAL_ERROR;
1746         }
1747
1748         *_compression_fmt = cmpr_state.format;
1749         return NT_STATUS_OK;
1750 }
1751
1752 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1753                                         TALLOC_CTX *mem_ctx,
1754                                         struct smb2_tree *tree,
1755                                         struct smb2_handle fh,
1756                                         uint16_t compression_fmt)
1757 {
1758         union smb_ioctl ioctl;
1759         struct compression_state cmpr_state;
1760         enum ndr_err_code ndr_ret;
1761         NTSTATUS status;
1762
1763         ZERO_STRUCT(ioctl);
1764         ioctl.smb2.level = RAW_IOCTL_SMB2;
1765         ioctl.smb2.in.file.handle = fh;
1766         ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1767         ioctl.smb2.in.max_response_size = 0;
1768         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1769
1770         cmpr_state.format = compression_fmt;
1771         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1772                                        &cmpr_state,
1773                         (ndr_push_flags_fn_t)ndr_push_compression_state);
1774         if (ndr_ret != NDR_ERR_SUCCESS) {
1775                 return NT_STATUS_INTERNAL_ERROR;
1776         }
1777
1778         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1779         return status;
1780 }
1781
1782 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1783                                             struct smb2_tree *tree)
1784 {
1785         struct smb2_handle fh;
1786         NTSTATUS status;
1787         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1788         bool ok;
1789         uint16_t compression_fmt;
1790
1791         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1792                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1793                                     FILE_ATTRIBUTE_NORMAL);
1794         torture_assert(torture, ok, "setup compression file");
1795
1796         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1797                                                   &ok);
1798         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1799         if (!ok) {
1800                 smb2_util_close(tree, fh);
1801                 torture_skip(torture, "FS compression not supported\n");
1802         }
1803
1804         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1805                                          &compression_fmt);
1806         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1807
1808         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1809                        "initial compression state not NONE");
1810
1811         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1812                                          COMPRESSION_FORMAT_DEFAULT);
1813         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1814
1815         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1816                                          &compression_fmt);
1817         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1818
1819         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1820                        "invalid compression state after set");
1821
1822         smb2_util_close(tree, fh);
1823         talloc_free(tmp_ctx);
1824         return true;
1825 }
1826
1827 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1828                                             struct smb2_tree *tree)
1829 {
1830         struct smb2_handle dirh;
1831         struct smb2_handle fh;
1832         NTSTATUS status;
1833         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1834         uint16_t compression_fmt;
1835         bool ok;
1836         char path_buf[PATH_MAX];
1837
1838         smb2_deltree(tree, DNAME);
1839         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1840                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1841                                     FILE_ATTRIBUTE_DIRECTORY);
1842         torture_assert(torture, ok, "setup compression directory");
1843
1844         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1845                                                   &ok);
1846         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1847         if (!ok) {
1848                 smb2_util_close(tree, dirh);
1849                 smb2_deltree(tree, DNAME);
1850                 torture_skip(torture, "FS compression not supported\n");
1851         }
1852
1853         /* set compression on parent dir, then check for inheritance */
1854         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1855                                          COMPRESSION_FORMAT_LZNT1);
1856         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1857
1858         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1859                                          &compression_fmt);
1860         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1861
1862         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1863                        "invalid compression state after set");
1864
1865         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1866         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1867                                     path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1868                                     FILE_ATTRIBUTE_NORMAL);
1869         torture_assert(torture, ok, "setup compression file");
1870
1871         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1872                                          &compression_fmt);
1873         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1874
1875         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1876                        "compression attr not inherited by new file");
1877
1878         /* check compressed data is consistent */
1879         ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1880
1881         /* disable dir compression attr, file should remain compressed */
1882         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1883                                          COMPRESSION_FORMAT_NONE);
1884         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1885
1886         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1887                                          &compression_fmt);
1888         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1889
1890         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1891                        "file compression attr removed after dir change");
1892         smb2_util_close(tree, fh);
1893
1894         /* new files should no longer inherit compression attr */
1895         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1896         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1897                                     path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1898                                     FILE_ATTRIBUTE_NORMAL);
1899         torture_assert(torture, ok, "setup file");
1900
1901         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1902                                          &compression_fmt);
1903         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1904
1905         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1906                        "compression attr present on new file");
1907
1908         smb2_util_close(tree, fh);
1909         smb2_util_close(tree, dirh);
1910         smb2_deltree(tree, DNAME);
1911         talloc_free(tmp_ctx);
1912         return true;
1913 }
1914
1915 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1916                                                struct smb2_tree *tree)
1917 {
1918         struct smb2_handle fh;
1919         NTSTATUS status;
1920         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1921         bool ok;
1922         uint16_t compression_fmt;
1923
1924         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1925                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1926                                     FILE_ATTRIBUTE_NORMAL);
1927         torture_assert(torture, ok, "setup compression file");
1928
1929         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1930                                                   &ok);
1931         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1932         if (!ok) {
1933                 smb2_util_close(tree, fh);
1934                 torture_skip(torture, "FS compression not supported\n");
1935         }
1936
1937         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1938                                          0x0042); /* bogus */
1939         torture_assert_ntstatus_equal(torture, status,
1940                                       NT_STATUS_INVALID_PARAMETER,
1941                                       "invalid FSCTL_SET_COMPRESSION");
1942
1943         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1944                                          &compression_fmt);
1945         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1946
1947         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1948                        "initial compression state not NONE");
1949
1950         smb2_util_close(tree, fh);
1951         talloc_free(tmp_ctx);
1952         return true;
1953 }
1954
1955 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1956                                             struct smb2_tree *tree)
1957 {
1958         struct smb2_handle fh;
1959         NTSTATUS status;
1960         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1961         bool ok;
1962         union smb_ioctl ioctl;
1963
1964         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1965                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1966                                     FILE_ATTRIBUTE_NORMAL);
1967         torture_assert(torture, ok, "setup compression file");
1968
1969         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1970                                                   &ok);
1971         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1972         if (!ok) {
1973                 smb2_util_close(tree, fh);
1974                 torture_skip(torture, "FS compression not supported\n");
1975         }
1976
1977         ZERO_STRUCT(ioctl);
1978         ioctl.smb2.level = RAW_IOCTL_SMB2;
1979         ioctl.smb2.in.file.handle = fh;
1980         ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1981         ioctl.smb2.in.max_response_size = 0;    /* no room for rsp data */
1982         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1983
1984         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1985         if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_USER_BUFFER)
1986          && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
1987                 /* neither Server 2k12 nor 2k8r2 response status */
1988                 torture_assert(torture, true,
1989                                "invalid FSCTL_SET_COMPRESSION");
1990         }
1991
1992         smb2_util_close(tree, fh);
1993         talloc_free(tmp_ctx);
1994         return true;
1995 }
1996
1997 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
1998                                                 struct smb2_tree *tree)
1999 {
2000         struct smb2_handle fh;
2001         union smb_fileinfo io;
2002         NTSTATUS status;
2003         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2004         bool ok;
2005
2006         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2007                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2008                                     FILE_ATTRIBUTE_NORMAL);
2009         torture_assert(torture, ok, "setup compression file");
2010
2011         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2012                                                   &ok);
2013         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2014         if (!ok) {
2015                 smb2_util_close(tree, fh);
2016                 torture_skip(torture, "FS compression not supported\n");
2017         }
2018
2019         ZERO_STRUCT(io);
2020         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2021         io.generic.in.file.handle = fh;
2022         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2023         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2024
2025         torture_assert(torture,
2026                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2027                        "compression attr before set");
2028
2029         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2030                                          COMPRESSION_FORMAT_DEFAULT);
2031         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2032
2033         ZERO_STRUCT(io);
2034         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2035         io.generic.in.file.handle = fh;
2036         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2037         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2038
2039         torture_assert(torture,
2040                        (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2041                        "no compression attr after set");
2042
2043         smb2_util_close(tree, fh);
2044         talloc_free(tmp_ctx);
2045         return true;
2046 }
2047
2048 /*
2049  * Specify FILE_ATTRIBUTE_COMPRESSED on creation, Windows does not retain this
2050  * attribute.
2051  */
2052 static bool test_ioctl_compress_create_with_attr(struct torture_context *torture,
2053                                                  struct smb2_tree *tree)
2054 {
2055         struct smb2_handle fh2;
2056         union smb_fileinfo io;
2057         NTSTATUS status;
2058         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2059         uint16_t compression_fmt;
2060         bool ok;
2061
2062         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2063                                     FNAME2, &fh2, 0, SEC_RIGHTS_FILE_ALL,
2064                         (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_COMPRESSED));
2065         torture_assert(torture, ok, "setup compression file");
2066
2067         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh2,
2068                                                   &ok);
2069         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2070         if (!ok) {
2071                 smb2_util_close(tree, fh2);
2072                 torture_skip(torture, "FS compression not supported\n");
2073         }
2074
2075         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh2,
2076                                          &compression_fmt);
2077         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2078
2079         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2080                        "initial compression state not NONE");
2081
2082         ZERO_STRUCT(io);
2083         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2084         io.generic.in.file.handle = fh2;
2085         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2086         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2087
2088         torture_assert(torture,
2089                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2090                        "incorrect compression attr");
2091
2092         smb2_util_close(tree, fh2);
2093         talloc_free(tmp_ctx);
2094         return true;
2095 }
2096
2097 static bool test_ioctl_compress_inherit_disable(struct torture_context *torture,
2098                                                 struct smb2_tree *tree)
2099 {
2100         struct smb2_handle fh;
2101         struct smb2_handle dirh;
2102         char path_buf[PATH_MAX];
2103         NTSTATUS status;
2104         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2105         bool ok;
2106         uint16_t compression_fmt;
2107
2108         struct smb2_create io;
2109
2110         smb2_deltree(tree, DNAME);
2111         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2112                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2113                                     FILE_ATTRIBUTE_DIRECTORY);
2114         torture_assert(torture, ok, "setup compression directory");
2115
2116         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
2117                                                   &ok);
2118         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2119         if (!ok) {
2120                 smb2_util_close(tree, dirh);
2121                 smb2_deltree(tree, DNAME);
2122                 torture_skip(torture, "FS compression not supported\n");
2123         }
2124
2125         /* set compression on parent dir, then check for inheritance */
2126         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
2127                                          COMPRESSION_FORMAT_LZNT1);
2128         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2129
2130         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2131                                          &compression_fmt);
2132         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2133
2134         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2135                        "invalid compression state after set");
2136         smb2_util_close(tree, dirh);
2137
2138         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
2139         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2140                                     path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
2141                                     FILE_ATTRIBUTE_NORMAL);
2142         torture_assert(torture, ok, "setup compression file");
2143
2144         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2145                                          &compression_fmt);
2146         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2147
2148         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
2149                        "compression attr not inherited by new file");
2150         smb2_util_close(tree, fh);
2151
2152         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
2153
2154         /* NO_COMPRESSION option should block inheritance */
2155         ZERO_STRUCT(io);
2156         io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2157         io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
2158         io.in.create_disposition = NTCREATEX_DISP_CREATE;
2159         io.in.create_options = NTCREATEX_OPTIONS_NO_COMPRESSION;
2160         io.in.share_access =
2161                 NTCREATEX_SHARE_ACCESS_DELETE|
2162                 NTCREATEX_SHARE_ACCESS_READ|
2163                 NTCREATEX_SHARE_ACCESS_WRITE;
2164         io.in.fname = path_buf;
2165
2166         status = smb2_create(tree, tmp_ctx, &io);
2167         torture_assert_ntstatus_ok(torture, status, "file create");
2168
2169         fh = io.out.file.handle;
2170
2171         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2172                                          &compression_fmt);
2173         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2174
2175         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2176                        "compression attr inherited by NO_COMPRESSION file");
2177         smb2_util_close(tree, fh);
2178
2179
2180         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, DNAME);
2181         ZERO_STRUCT(io);
2182         io.in.desired_access = SEC_RIGHTS_FILE_ALL;
2183         io.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
2184         io.in.create_disposition = NTCREATEX_DISP_CREATE;
2185         io.in.create_options = (NTCREATEX_OPTIONS_NO_COMPRESSION
2186                                 | NTCREATEX_OPTIONS_DIRECTORY);
2187         io.in.share_access =
2188                 NTCREATEX_SHARE_ACCESS_DELETE|
2189                 NTCREATEX_SHARE_ACCESS_READ|
2190                 NTCREATEX_SHARE_ACCESS_WRITE;
2191         io.in.fname = path_buf;
2192
2193         status = smb2_create(tree, tmp_ctx, &io);
2194         torture_assert_ntstatus_ok(torture, status, "dir create");
2195
2196         dirh = io.out.file.handle;
2197
2198         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2199                                          &compression_fmt);
2200         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2201
2202         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2203                        "compression attr inherited by NO_COMPRESSION dir");
2204         smb2_util_close(tree, dirh);
2205         smb2_deltree(tree, DNAME);
2206
2207         talloc_free(tmp_ctx);
2208         return true;
2209 }
2210
2211 /* attempting to set compression via SetInfo should not stick */
2212 static bool test_ioctl_compress_set_file_attr(struct torture_context *torture,
2213                                               struct smb2_tree *tree)
2214 {
2215         struct smb2_handle fh;
2216         struct smb2_handle dirh;
2217         union smb_fileinfo io;
2218         union smb_setfileinfo set_io;
2219         uint16_t compression_fmt;
2220         NTSTATUS status;
2221         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2222         bool ok;
2223
2224         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2225                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2226                                     FILE_ATTRIBUTE_NORMAL);
2227         torture_assert(torture, ok, "setup compression file");
2228
2229         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2230                                                   &ok);
2231         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2232         if (!ok) {
2233                 smb2_util_close(tree, fh);
2234                 torture_skip(torture, "FS compression not supported\n");
2235         }
2236
2237         ZERO_STRUCT(io);
2238         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2239         io.generic.in.file.handle = fh;
2240         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2241         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2242
2243         torture_assert(torture,
2244                 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2245                        "compression attr before set");
2246
2247         ZERO_STRUCT(set_io);
2248         set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2249         set_io.basic_info.in.file.handle = fh;
2250         set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2251         set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2252         set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2253         set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2254         set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2255                                                 | FILE_ATTRIBUTE_COMPRESSED);
2256         status = smb2_setinfo_file(tree, &set_io);
2257         torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2258
2259         ZERO_STRUCT(io);
2260         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2261         io.generic.in.file.handle = fh;
2262         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2263         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2264
2265         torture_assert(torture,
2266                 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2267                 "compression attr after set");
2268
2269         smb2_util_close(tree, fh);
2270         smb2_deltree(tree, DNAME);
2271         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2272                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2273                                     FILE_ATTRIBUTE_DIRECTORY);
2274         torture_assert(torture, ok, "setup compression directory");
2275
2276         ZERO_STRUCT(io);
2277         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2278         io.generic.in.file.handle = dirh;
2279         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2280         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2281
2282         torture_assert(torture,
2283                 ((io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
2284                        "compression attr before set");
2285
2286         ZERO_STRUCT(set_io);
2287         set_io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2288         set_io.basic_info.in.file.handle = dirh;
2289         set_io.basic_info.in.create_time = io.basic_info.out.create_time;
2290         set_io.basic_info.in.access_time = io.basic_info.out.access_time;
2291         set_io.basic_info.in.write_time = io.basic_info.out.write_time;
2292         set_io.basic_info.in.change_time = io.basic_info.out.change_time;
2293         set_io.basic_info.in.attrib = (io.basic_info.out.attrib
2294                                                 | FILE_ATTRIBUTE_COMPRESSED);
2295         status = smb2_setinfo_file(tree, &set_io);
2296         torture_assert_ntstatus_ok(torture, status, "SMB2_SETINFO_FILE");
2297
2298         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
2299                                          &compression_fmt);
2300         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2301
2302         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2303                        "dir compression set after SetInfo");
2304
2305         smb2_util_close(tree, dirh);
2306         talloc_free(tmp_ctx);
2307         return true;
2308 }
2309
2310 static bool test_ioctl_compress_perms(struct torture_context *torture,
2311                                       struct smb2_tree *tree)
2312 {
2313         struct smb2_handle fh;
2314         uint16_t compression_fmt;
2315         union smb_fileinfo io;
2316         NTSTATUS status;
2317         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2318         bool ok;
2319
2320         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2321                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2322                                     FILE_ATTRIBUTE_NORMAL);
2323         torture_assert(torture, ok, "setup compression file");
2324
2325         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
2326                                                   &ok);
2327         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2328         smb2_util_close(tree, fh);
2329         if (!ok) {
2330                 torture_skip(torture, "FS compression not supported\n");
2331         }
2332
2333         /* attempt get compression without READ_ATTR permission */
2334         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2335                                     FNAME, &fh, 0,
2336                         (SEC_RIGHTS_FILE_READ & ~(SEC_FILE_READ_ATTRIBUTE
2337                                                         | SEC_STD_READ_CONTROL
2338                                                         | SEC_FILE_READ_EA)),
2339                                     FILE_ATTRIBUTE_NORMAL);
2340         torture_assert(torture, ok, "setup compression file");
2341
2342         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2343                                          &compression_fmt);
2344         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2345         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2346                        "compression set after create");
2347         smb2_util_close(tree, fh);
2348
2349         /* set compression without WRITE_ATTR permission should succeed */
2350         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2351                                     FNAME, &fh, 0,
2352                         (SEC_RIGHTS_FILE_WRITE & ~(SEC_FILE_WRITE_ATTRIBUTE
2353                                                         | SEC_STD_WRITE_DAC
2354                                                         | SEC_FILE_WRITE_EA)),
2355                                     FILE_ATTRIBUTE_NORMAL);
2356         torture_assert(torture, ok, "setup compression file");
2357
2358         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2359                                          COMPRESSION_FORMAT_DEFAULT);
2360         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
2361         smb2_util_close(tree, fh);
2362
2363         ok = test_setup_open(torture, tree, tmp_ctx,
2364                                     FNAME, &fh, SEC_RIGHTS_FILE_ALL,
2365                                     FILE_ATTRIBUTE_NORMAL);
2366         torture_assert(torture, ok, "setup compression file");
2367         ZERO_STRUCT(io);
2368         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2369         io.generic.in.file.handle = fh;
2370         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2371         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2372
2373         torture_assert(torture,
2374                        (io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
2375                        "incorrect compression attr");
2376         smb2_util_close(tree, fh);
2377
2378         /* attempt get compression without READ_DATA permission */
2379         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2380                                     FNAME, &fh, 0,
2381                         (SEC_RIGHTS_FILE_READ & ~SEC_FILE_READ_DATA),
2382                                     FILE_ATTRIBUTE_NORMAL);
2383         torture_assert(torture, ok, "setup compression file");
2384
2385         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2386                                          &compression_fmt);
2387         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2388         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2389                        "compression enabled after set");
2390         smb2_util_close(tree, fh);
2391
2392         /* attempt get compression with only SYNCHRONIZE permission */
2393         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2394                                     FNAME, &fh, 0,
2395                                     SEC_STD_SYNCHRONIZE,
2396                                     FILE_ATTRIBUTE_NORMAL);
2397         torture_assert(torture, ok, "setup compression file");
2398
2399         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
2400                                          &compression_fmt);
2401         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
2402         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
2403                        "compression not enabled after set");
2404         smb2_util_close(tree, fh);
2405
2406         /* attempt to set compression without WRITE_DATA permission */
2407         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2408                                     FNAME, &fh, 0,
2409                         (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2410                                     FILE_ATTRIBUTE_NORMAL);
2411         torture_assert(torture, ok, "setup compression file");
2412
2413         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2414                                          COMPRESSION_FORMAT_DEFAULT);
2415         torture_assert_ntstatus_equal(torture, status,
2416                                       NT_STATUS_ACCESS_DENIED,
2417                                       "FSCTL_SET_COMPRESSION permission");
2418         smb2_util_close(tree, fh);
2419
2420         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2421                                     FNAME, &fh, 0,
2422                         (SEC_RIGHTS_FILE_WRITE & (~SEC_FILE_WRITE_DATA)),
2423                                     FILE_ATTRIBUTE_NORMAL);
2424         torture_assert(torture, ok, "setup compression file");
2425
2426         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
2427                                          COMPRESSION_FORMAT_NONE);
2428         torture_assert_ntstatus_equal(torture, status,
2429                                       NT_STATUS_ACCESS_DENIED,
2430                                       "FSCTL_SET_COMPRESSION permission");
2431         smb2_util_close(tree, fh);
2432
2433         talloc_free(tmp_ctx);
2434         return true;
2435 }
2436
2437 /*
2438    basic testing of the SMB2 FSCTL_QUERY_NETWORK_INTERFACE_INFO ioctl
2439 */
2440 static bool test_ioctl_network_interface_info(struct torture_context *torture,
2441                                       struct smb2_tree *tree)
2442 {
2443         union smb_ioctl ioctl;
2444         struct smb2_handle fh;
2445         NTSTATUS status;
2446         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2447         struct fsctl_net_iface_info net_iface;
2448         enum ndr_err_code ndr_ret;
2449         uint32_t caps;
2450
2451         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2452         if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
2453                 torture_skip(torture, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
2454         }
2455
2456         ZERO_STRUCT(ioctl);
2457         ioctl.smb2.level = RAW_IOCTL_SMB2;
2458         fh.data[0] = UINT64_MAX;
2459         fh.data[1] = UINT64_MAX;
2460         ioctl.smb2.in.file.handle = fh;
2461         ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
2462         ioctl.smb2.in.max_response_size = 0x10000; /* Windows client sets this to 64KiB */
2463         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2464
2465         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2466         torture_assert_ntstatus_ok(torture, status, "FSCTL_QUERY_NETWORK_INTERFACE_INFO");
2467
2468         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &net_iface,
2469                         (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info);
2470         torture_assert_ndr_success(torture, ndr_ret,
2471                                    "ndr_pull_fsctl_net_iface_info");
2472
2473         ndr_print_debug((ndr_print_fn_t)ndr_print_fsctl_net_iface_info,
2474                         "Network Interface Info", &net_iface);
2475
2476         talloc_free(tmp_ctx);
2477         return true;
2478 }
2479
2480 static NTSTATUS test_ioctl_sparse_fs_supported(struct torture_context *torture,
2481                                                struct smb2_tree *tree,
2482                                                TALLOC_CTX *mem_ctx,
2483                                                struct smb2_handle *fh,
2484                                                bool *sparse_support)
2485 {
2486         NTSTATUS status;
2487         union smb_fsinfo info;
2488
2489         ZERO_STRUCT(info);
2490         info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
2491         info.generic.handle = *fh;
2492         status = smb2_getinfo_fs(tree, tree, &info);
2493         if (!NT_STATUS_IS_OK(status)) {
2494                 return status;
2495         }
2496
2497         if (info.attribute_info.out.fs_attr & FILE_SUPPORTS_SPARSE_FILES) {
2498                 *sparse_support = true;
2499         } else {
2500                 *sparse_support = false;
2501         }
2502         return NT_STATUS_OK;
2503 }
2504
2505 static NTSTATUS test_ioctl_sparse_req(struct torture_context *torture,
2506                                       TALLOC_CTX *mem_ctx,
2507                                       struct smb2_tree *tree,
2508                                       struct smb2_handle fh,
2509                                       bool set)
2510 {
2511         union smb_ioctl ioctl;
2512         NTSTATUS status;
2513         uint8_t set_sparse;
2514
2515         ZERO_STRUCT(ioctl);
2516         ioctl.smb2.level = RAW_IOCTL_SMB2;
2517         ioctl.smb2.in.file.handle = fh;
2518         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2519         ioctl.smb2.in.max_response_size = 0;
2520         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2521         set_sparse = (set ? 0xFF : 0x0);
2522         ioctl.smb2.in.out.data = &set_sparse;
2523         ioctl.smb2.in.out.length = sizeof(set_sparse);
2524
2525         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
2526         return status;
2527 }
2528
2529 static NTSTATUS test_sparse_get(struct torture_context *torture,
2530                                 TALLOC_CTX *mem_ctx,
2531                                 struct smb2_tree *tree,
2532                                 struct smb2_handle fh,
2533                                 bool *_is_sparse)
2534 {
2535         union smb_fileinfo io;
2536         NTSTATUS status;
2537
2538         ZERO_STRUCT(io);
2539         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
2540         io.generic.in.file.handle = fh;
2541         status = smb2_getinfo_file(tree, mem_ctx, &io);
2542         if (!NT_STATUS_IS_OK(status)) {
2543                 return status;
2544         }
2545         *_is_sparse = !!(io.basic_info.out.attrib & FILE_ATTRIBUTE_SPARSE);
2546
2547         return status;
2548 }
2549
2550 static bool test_ioctl_sparse_file_flag(struct torture_context *torture,
2551                                         struct smb2_tree *tree)
2552 {
2553         struct smb2_handle fh;
2554         union smb_fileinfo io;
2555         NTSTATUS status;
2556         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2557         bool ok;
2558         bool is_sparse;
2559
2560         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2561                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2562                                     FILE_ATTRIBUTE_NORMAL);
2563         torture_assert(torture, ok, "setup file");
2564
2565         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2566                                                 &ok);
2567         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2568         if (!ok) {
2569                 smb2_util_close(tree, fh);
2570                 torture_skip(torture, "Sparse files not supported\n");
2571         }
2572
2573         ZERO_STRUCT(io);
2574         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2575         io.generic.in.file.handle = fh;
2576         status = smb2_getinfo_file(tree, tmp_ctx, &io);
2577         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
2578
2579         torture_assert(torture,
2580                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_SPARSE) == 0),
2581                        "sparse attr before set");
2582
2583         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2584         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2585
2586         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2587         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2588         torture_assert(torture, is_sparse, "no sparse attr after set");
2589
2590         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2591         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2592
2593         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2594         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2595         torture_assert(torture, !is_sparse, "sparse attr after unset");
2596
2597         smb2_util_close(tree, fh);
2598         talloc_free(tmp_ctx);
2599         return true;
2600 }
2601
2602 static bool test_ioctl_sparse_file_attr(struct torture_context *torture,
2603                                         struct smb2_tree *tree)
2604 {
2605         struct smb2_handle fh;
2606         NTSTATUS status;
2607         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2608         bool ok;
2609         bool is_sparse;
2610
2611         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2612                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2613                         (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_SPARSE));
2614         torture_assert(torture, ok, "setup file");
2615
2616         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2617                                                 &ok);
2618         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2619         if (!ok) {
2620                 smb2_util_close(tree, fh);
2621                 torture_skip(torture, "Sparse files not supported\n");
2622         }
2623
2624         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2625         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2626         torture_assert(torture, !is_sparse, "sparse attr on open");
2627
2628         smb2_util_close(tree, fh);
2629         talloc_free(tmp_ctx);
2630         return true;
2631 }
2632
2633 static bool test_ioctl_sparse_dir_flag(struct torture_context *torture,
2634                                         struct smb2_tree *tree)
2635 {
2636         struct smb2_handle dirh;
2637         NTSTATUS status;
2638         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2639         bool ok;
2640
2641         smb2_deltree(tree, DNAME);
2642         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2643                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
2644                                     FILE_ATTRIBUTE_DIRECTORY);
2645         torture_assert(torture, ok, "setup sparse directory");
2646
2647         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &dirh,
2648                                                 &ok);
2649         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2650         if (!ok) {
2651                 smb2_util_close(tree, dirh);
2652                 smb2_deltree(tree, DNAME);
2653                 torture_skip(torture, "Sparse files not supported\n");
2654         }
2655
2656         /* set sparse dir should fail, check for 2k12 & 2k8 response */
2657         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, dirh, true);
2658         torture_assert_ntstatus_equal(torture, status,
2659                                       NT_STATUS_INVALID_PARAMETER,
2660                                       "dir FSCTL_SET_SPARSE status");
2661
2662         smb2_util_close(tree, dirh);
2663         smb2_deltree(tree, DNAME);
2664         talloc_free(tmp_ctx);
2665         return true;
2666 }
2667
2668 /*
2669  * FSCTL_SET_SPARSE can be sent with (already tested) or without a SetSparse
2670  * buffer to indicate whether the flag should be set or cleared. When sent
2671  * without a buffer, it must be handled as if SetSparse=TRUE.
2672  */
2673 static bool test_ioctl_sparse_set_nobuf(struct torture_context *torture,
2674                                         struct smb2_tree *tree)
2675 {
2676         struct smb2_handle fh;
2677         union smb_ioctl ioctl;
2678         NTSTATUS status;
2679         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2680         bool ok;
2681         bool is_sparse;
2682
2683         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2684                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2685                                     FILE_ATTRIBUTE_NORMAL);
2686         torture_assert(torture, ok, "setup file");
2687
2688         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2689                                                 &ok);
2690         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2691         if (!ok) {
2692                 smb2_util_close(tree, fh);
2693                 torture_skip(torture, "Sparse files not supported\n");
2694         }
2695
2696         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2697         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2698         torture_assert(torture, !is_sparse, "sparse attr before set");
2699
2700         ZERO_STRUCT(ioctl);
2701         ioctl.smb2.level = RAW_IOCTL_SMB2;
2702         ioctl.smb2.in.file.handle = fh;
2703         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2704         ioctl.smb2.in.max_response_size = 0;
2705         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2706         /* ioctl.smb2.in.out is zeroed, no SetSparse buffer */
2707
2708         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2709         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2710
2711         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2712         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2713         torture_assert(torture, is_sparse, "no sparse attr after set");
2714
2715         /* second non-SetSparse request shouldn't toggle sparse */
2716         ZERO_STRUCT(ioctl);
2717         ioctl.smb2.level = RAW_IOCTL_SMB2;
2718         ioctl.smb2.in.file.handle = fh;
2719         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2720         ioctl.smb2.in.max_response_size = 0;
2721         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2722
2723         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2724         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2725
2726         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2727         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2728         torture_assert(torture, is_sparse, "no sparse attr after 2nd set");
2729
2730         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, false);
2731         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2732
2733         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2734         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2735         torture_assert(torture, !is_sparse, "sparse attr after unset");
2736
2737         smb2_util_close(tree, fh);
2738         talloc_free(tmp_ctx);
2739         return true;
2740 }
2741
2742 static bool test_ioctl_sparse_set_oversize(struct torture_context *torture,
2743                                            struct smb2_tree *tree)
2744 {
2745         struct smb2_handle fh;
2746         union smb_ioctl ioctl;
2747         NTSTATUS status;
2748         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2749         bool ok;
2750         bool is_sparse;
2751         uint8_t buf[100];
2752
2753         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2754                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2755                                     FILE_ATTRIBUTE_NORMAL);
2756         torture_assert(torture, ok, "setup file");
2757
2758         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2759                                                 &ok);
2760         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2761         if (!ok) {
2762                 smb2_util_close(tree, fh);
2763                 torture_skip(torture, "Sparse files not supported\n");
2764         }
2765
2766         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2767         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2768         torture_assert(torture, !is_sparse, "sparse attr before set");
2769
2770         ZERO_STRUCT(ioctl);
2771         ioctl.smb2.level = RAW_IOCTL_SMB2;
2772         ioctl.smb2.in.file.handle = fh;
2773         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2774         ioctl.smb2.in.max_response_size = 0;
2775         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2776
2777         /*
2778          * Attach a request buffer larger than FILE_SET_SPARSE_BUFFER
2779          * Windows still successfully processes the request.
2780          */
2781         ZERO_ARRAY(buf);
2782         buf[0] = 0xFF; /* attempt to set sparse */
2783         ioctl.smb2.in.out.data = buf;
2784         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2785
2786         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2787         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2788
2789         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2790         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2791         torture_assert(torture, is_sparse, "no sparse attr after set");
2792
2793         ZERO_STRUCT(ioctl);
2794         ioctl.smb2.level = RAW_IOCTL_SMB2;
2795         ioctl.smb2.in.file.handle = fh;
2796         ioctl.smb2.in.function = FSCTL_SET_SPARSE;
2797         ioctl.smb2.in.max_response_size = 0;
2798         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2799
2800         ZERO_ARRAY(buf); /* clear sparse */
2801         ioctl.smb2.in.out.data = buf;
2802         ioctl.smb2.in.out.length = ARRAY_SIZE(buf);
2803
2804         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2805         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2806
2807         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2808         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2809         torture_assert(torture, !is_sparse, "sparse attr after clear");
2810
2811         smb2_util_close(tree, fh);
2812         talloc_free(tmp_ctx);
2813         return true;
2814 }
2815
2816 static NTSTATUS test_ioctl_qar_req(struct torture_context *torture,
2817                                    TALLOC_CTX *mem_ctx,
2818                                    struct smb2_tree *tree,
2819                                    struct smb2_handle fh,
2820                                    int64_t req_off,
2821                                    int64_t req_len,
2822                                    struct file_alloced_range_buf **_rsp,
2823                                    uint64_t *_rsp_count)
2824 {
2825         union smb_ioctl ioctl;
2826         NTSTATUS status;
2827         enum ndr_err_code ndr_ret;
2828         struct file_alloced_range_buf far_buf;
2829         struct file_alloced_range_buf *far_rsp = NULL;
2830         uint64_t far_count = 0;
2831         int i;
2832         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
2833         if (tmp_ctx == NULL) {
2834                 return NT_STATUS_NO_MEMORY;
2835         }
2836
2837         ZERO_STRUCT(ioctl);
2838         ioctl.smb2.level = RAW_IOCTL_SMB2;
2839         ioctl.smb2.in.file.handle = fh;
2840         ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
2841         ioctl.smb2.in.max_response_size = 1024;
2842         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
2843
2844         far_buf.file_off = req_off;
2845         far_buf.len = req_len;
2846
2847         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
2848                                        &far_buf,
2849                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
2850         if (ndr_ret != NDR_ERR_SUCCESS) {
2851                 status = NT_STATUS_UNSUCCESSFUL;
2852                 goto err_out;
2853         }
2854
2855         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
2856         if (!NT_STATUS_IS_OK(status)) {
2857                 goto err_out;
2858         }
2859
2860         if (ioctl.smb2.out.out.length == 0) {
2861                 goto done;
2862         }
2863
2864         if ((ioctl.smb2.out.out.length % sizeof(far_buf)) != 0) {
2865                 torture_comment(torture, "invalid qry_alloced rsp len: %zd:",
2866                                 ioctl.smb2.out.out.length);
2867                 status = NT_STATUS_INVALID_VIEW_SIZE;
2868                 goto err_out;
2869         }
2870
2871         far_count = (ioctl.smb2.out.out.length / sizeof(far_buf));
2872         far_rsp = talloc_array(mem_ctx, struct file_alloced_range_buf,
2873                                far_count);
2874         if (far_rsp == NULL) {
2875                 status = NT_STATUS_NO_MEMORY;
2876                 goto err_out;
2877         }
2878
2879         for (i = 0; i < far_count; i++) {
2880                 ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
2881                                                &far_rsp[i],
2882                         (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
2883                 if (ndr_ret != NDR_ERR_SUCCESS) {
2884                         status = NT_STATUS_UNSUCCESSFUL;
2885                         goto err_out;
2886                 }
2887         }
2888
2889 done:
2890         *_rsp = far_rsp;
2891         *_rsp_count = far_count;
2892         status = NT_STATUS_OK;
2893 err_out:
2894         talloc_free(tmp_ctx);
2895         return status;
2896 }
2897
2898 static bool test_ioctl_sparse_qar(struct torture_context *torture,
2899                                   struct smb2_tree *tree)
2900 {
2901         struct smb2_handle fh;
2902         NTSTATUS status;
2903         TALLOC_CTX *tmp_ctx = talloc_new(tree);
2904         bool ok;
2905         bool is_sparse;
2906         struct file_alloced_range_buf *far_rsp = NULL;
2907         uint64_t far_count = 0;
2908
2909         /* zero length file, shouldn't have any ranges */
2910         ok = test_setup_create_fill(torture, tree, tmp_ctx,
2911                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
2912                                     FILE_ATTRIBUTE_NORMAL);
2913         torture_assert(torture, ok, "setup file");
2914
2915         status = test_ioctl_sparse_fs_supported(torture, tree, tmp_ctx, &fh,
2916                                                 &ok);
2917         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
2918         if (!ok) {
2919                 smb2_util_close(tree, fh);
2920                 torture_skip(torture, "Sparse files not supported\n");
2921         }
2922
2923         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2924         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2925         torture_assert(torture, !is_sparse, "sparse attr before set");
2926
2927         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2928                                     0,  /* off */
2929                                     0,  /* len */
2930                                     &far_rsp,
2931                                     &far_count);
2932         torture_assert_ntstatus_ok(torture, status,
2933                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2934         torture_assert_u64_equal(torture, far_count, 0,
2935                                  "unexpected response len");
2936
2937         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2938                                     0,  /* off */
2939                                     1024,       /* len */
2940                                     &far_rsp,
2941                                     &far_count);
2942         torture_assert_ntstatus_ok(torture, status,
2943                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2944         torture_assert_u64_equal(torture, far_count, 0,
2945                                  "unexpected response len");
2946
2947         status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
2948         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
2949
2950         status = test_sparse_get(torture, tmp_ctx, tree, fh, &is_sparse);
2951         torture_assert_ntstatus_ok(torture, status, "test_sparse_get");
2952         torture_assert(torture, is_sparse, "no sparse attr after set");
2953
2954         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2955                                     0,  /* off */
2956                                     1024,       /* len */
2957                                     &far_rsp,
2958                                     &far_count);
2959         torture_assert_ntstatus_ok(torture, status,
2960                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2961         torture_assert_u64_equal(torture, far_count, 0,
2962                                  "unexpected response len");
2963
2964         /* write into the (now) sparse file at 4k offset */
2965         ok = write_pattern(torture, tree, tmp_ctx, fh,
2966                            4096,        /* off */
2967                            1024,        /* len */
2968                            4096);       /* pattern offset */
2969         torture_assert(torture, ok, "write pattern");
2970
2971         /* query range before write off, it should be alloced */
2972         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2973                                     0,  /* off */
2974                                     4096,       /* len */
2975                                     &far_rsp,
2976                                     &far_count);
2977         torture_assert_ntstatus_ok(torture, status,
2978                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2979         torture_assert_u64_equal(torture, far_count, 1,
2980                                  "unexpected response len");
2981         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
2982         torture_assert_u64_equal(torture, far_rsp[0].len, 4096, "far len");
2983
2984         /*
2985          * Query range before and past write, it should be allocated up to the
2986          * end of the write.
2987          */
2988         status = test_ioctl_qar_req(torture, tmp_ctx, tree, fh,
2989                                     0,  /* off */
2990                                     8192,       /* len */
2991                                     &far_rsp,
2992                                     &far_count);
2993         torture_assert_ntstatus_ok(torture, status,
2994                                    "FSCTL_QUERY_ALLOCATED_RANGES req failed");
2995         torture_assert_u64_equal(torture, far_count, 1,
2996                                  "unexpected response len");
2997         torture_assert_u64_equal(torture, far_rsp[0].file_off, 0, "far offset");
2998         torture_assert_u64_equal(torture, far_rsp[0].len, 5120, "far len");
2999
3000         smb2_util_close(tree, fh);
3001         talloc_free(tmp_ctx);
3002         return true;
3003 }
3004
3005 /*
3006  * basic testing of SMB2 ioctls
3007  */
3008 struct torture_suite *torture_smb2_ioctl_init(void)
3009 {
3010         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
3011
3012         torture_suite_add_1smb2_test(suite, "shadow_copy",
3013                                      test_ioctl_get_shadow_copy);
3014         torture_suite_add_1smb2_test(suite, "req_resume_key",
3015                                      test_ioctl_req_resume_key);
3016         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
3017                                      test_ioctl_copy_chunk_simple);
3018         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
3019                                      test_ioctl_copy_chunk_multi);
3020         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
3021                                      test_ioctl_copy_chunk_tiny);
3022         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
3023                                      test_ioctl_copy_chunk_over);
3024         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
3025                                      test_ioctl_copy_chunk_append);
3026         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
3027                                      test_ioctl_copy_chunk_limits);
3028         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
3029                                      test_ioctl_copy_chunk_src_lck);
3030         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
3031                                      test_ioctl_copy_chunk_dest_lck);
3032         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
3033                                      test_ioctl_copy_chunk_bad_key);
3034         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
3035                                      test_ioctl_copy_chunk_src_is_dest);
3036         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
3037                                      test_ioctl_copy_chunk_src_is_dest_overlap);
3038         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
3039                                      test_ioctl_copy_chunk_bad_access);
3040         torture_suite_add_1smb2_test(suite, "copy_chunk_write_access",
3041                                      test_ioctl_copy_chunk_write_access);
3042         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
3043                                      test_ioctl_copy_chunk_src_exceed);
3044         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
3045                                      test_ioctl_copy_chunk_src_exceed_multi);
3046         torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
3047                                      test_ioctl_copy_chunk_sparse_dest);
3048         torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
3049                                      test_ioctl_copy_chunk_max_output_sz);
3050         torture_suite_add_1smb2_test(suite, "copy_chunk_zero_length",
3051                                      test_ioctl_copy_chunk_zero_length);
3052         torture_suite_add_1smb2_test(suite, "compress_file_flag",
3053                                      test_ioctl_compress_file_flag);
3054         torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
3055                                      test_ioctl_compress_dir_inherit);
3056         torture_suite_add_1smb2_test(suite, "compress_invalid_format",
3057                                      test_ioctl_compress_invalid_format);
3058         torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
3059                                      test_ioctl_compress_invalid_buf);
3060         torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
3061                                      test_ioctl_compress_query_file_attr);
3062         torture_suite_add_1smb2_test(suite, "compress_create_with_attr",
3063                                      test_ioctl_compress_create_with_attr);
3064         torture_suite_add_1smb2_test(suite, "compress_inherit_disable",
3065                                      test_ioctl_compress_inherit_disable);
3066         torture_suite_add_1smb2_test(suite, "compress_set_file_attr",
3067                                      test_ioctl_compress_set_file_attr);
3068         torture_suite_add_1smb2_test(suite, "compress_perms",
3069                                      test_ioctl_compress_perms);
3070         torture_suite_add_1smb2_test(suite, "network_interface_info",
3071                                      test_ioctl_network_interface_info);
3072         torture_suite_add_1smb2_test(suite, "sparse_file_flag",
3073                                      test_ioctl_sparse_file_flag);
3074         torture_suite_add_1smb2_test(suite, "sparse_file_attr",
3075                                      test_ioctl_sparse_file_attr);
3076         torture_suite_add_1smb2_test(suite, "sparse_dir_flag",
3077                                      test_ioctl_sparse_dir_flag);
3078         torture_suite_add_1smb2_test(suite, "sparse_set_nobuf",
3079                                      test_ioctl_sparse_set_nobuf);
3080         torture_suite_add_1smb2_test(suite, "sparse_set_oversize",
3081                                      test_ioctl_sparse_set_oversize);
3082         torture_suite_add_1smb2_test(suite, "sparse_qar",
3083                                      test_ioctl_sparse_qar);
3084
3085         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
3086
3087         return suite;
3088 }
3089