torture: add file_attribs arg to file create helper
[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 "librpc/gen_ndr/ndr_ioctl.h"
29
30 #define FNAME   "testfsctl.dat"
31 #define FNAME2  "testfsctl2.dat"
32 #define DNAME   "testfsctl_dir"
33
34 /*
35    basic testing of SMB2 shadow copy calls
36 */
37 static bool test_ioctl_get_shadow_copy(struct torture_context *torture,
38                                        struct smb2_tree *tree)
39 {
40         struct smb2_handle h;
41         uint8_t buf[100];
42         NTSTATUS status;
43         union smb_ioctl ioctl;
44         TALLOC_CTX *tmp_ctx = talloc_new(tree);
45
46         smb2_util_unlink(tree, FNAME);
47
48         status = torture_smb2_testfile(tree, FNAME, &h);
49         torture_assert_ntstatus_ok(torture, status, "create write");
50
51         ZERO_ARRAY(buf);
52         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
53         torture_assert_ntstatus_ok(torture, status, "write");
54
55         ZERO_STRUCT(ioctl);
56         ioctl.smb2.level = RAW_IOCTL_SMB2;
57         ioctl.smb2.in.file.handle = h;
58         ioctl.smb2.in.function = FSCTL_SRV_ENUM_SNAPS;
59         ioctl.smb2.in.max_response_size = 16;
60         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
61
62         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
63         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)
64          || NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST)) {
65                 torture_skip(torture, "FSCTL_SRV_ENUM_SNAPS not supported\n");
66         }
67         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_ENUM_SNAPS");
68
69         return true;
70 }
71
72 /*
73    basic testing of the SMB2 server side copy ioctls
74 */
75 static bool test_ioctl_req_resume_key(struct torture_context *torture,
76                                       struct smb2_tree *tree)
77 {
78         struct smb2_handle h;
79         uint8_t buf[100];
80         NTSTATUS status;
81         union smb_ioctl ioctl;
82         TALLOC_CTX *tmp_ctx = talloc_new(tree);
83         struct req_resume_key_rsp res_key;
84         enum ndr_err_code ndr_ret;
85
86         smb2_util_unlink(tree, FNAME);
87
88         status = torture_smb2_testfile(tree, FNAME, &h);
89         torture_assert_ntstatus_ok(torture, status, "create write");
90
91         ZERO_ARRAY(buf);
92         status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
93         torture_assert_ntstatus_ok(torture, status, "write");
94
95         ZERO_STRUCT(ioctl);
96         ioctl.smb2.level = RAW_IOCTL_SMB2;
97         ioctl.smb2.in.file.handle = h;
98         ioctl.smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
99         ioctl.smb2.in.max_response_size = 32;
100         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
101
102         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
103         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_REQUEST_RESUME_KEY");
104
105         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx, &res_key,
106                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
107         torture_assert_ndr_success(torture, ndr_ret,
108                                    "ndr_pull_req_resume_key_rsp");
109
110         ndr_print_debug((ndr_print_fn_t)ndr_print_req_resume_key_rsp, "yo", &res_key);
111
112         talloc_free(tmp_ctx);
113         return true;
114 }
115
116 static uint64_t patt_hash(uint64_t off)
117 {
118         return off;
119 }
120
121 static bool check_pattern(struct torture_context *torture,
122                           struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
123                           struct smb2_handle h, uint64_t off, uint64_t len,
124                           uint64_t patt_off)
125 {
126         uint64_t i;
127         struct smb2_read r;
128         NTSTATUS status;
129
130         ZERO_STRUCT(r);
131         r.in.file.handle = h;
132         r.in.length      = len;
133         r.in.offset      = off;
134         status = smb2_read(tree, mem_ctx, &r);
135         torture_assert_ntstatus_ok(torture, status, "read");
136
137         torture_assert_u64_equal(torture, r.out.data.length, len,
138                                  "read data len mismatch");
139
140         for (i = 0; i <= len - 8; i += 8, patt_off += 8) {
141                 uint64_t data = BVAL(r.out.data.data, i);
142                 torture_assert_u64_equal(torture, data, patt_hash(patt_off),
143                                          talloc_asprintf(torture, "read data "
144                                                          "pattern bad at %llu\n",
145                                                          (unsigned long long)i));
146         }
147
148         talloc_free(r.out.data.data);
149         return true;
150 }
151
152 static bool test_setup_create_fill(struct torture_context *torture,
153                                    struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
154                                    const char *fname,
155                                    struct smb2_handle *fh,
156                                    uint64_t size,
157                                    uint32_t desired_access,
158                                    uint32_t file_attributes)
159 {
160         struct smb2_create io;
161         NTSTATUS status;
162         uint64_t i;
163         uint8_t *buf = talloc_zero_size(mem_ctx, size);
164         torture_assert(torture, (buf != NULL), "no memory for file data buf");
165
166         smb2_util_unlink(tree, fname);
167
168         ZERO_STRUCT(io);
169         io.in.desired_access = desired_access;
170         io.in.file_attributes = file_attributes;
171         io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
172         io.in.share_access =
173                 NTCREATEX_SHARE_ACCESS_DELETE|
174                 NTCREATEX_SHARE_ACCESS_READ|
175                 NTCREATEX_SHARE_ACCESS_WRITE;
176         io.in.fname = fname;
177
178         status = smb2_create(tree, mem_ctx, &io);
179         torture_assert_ntstatus_ok(torture, status, "file create");
180
181         *fh = io.out.file.handle;
182
183         if (size > 0) {
184                 uint64_t cur_off = 0;
185                 for (i = 0; i <= size - 8; i += 8) {
186                         SBVAL(buf, i, patt_hash(i));
187                 }
188                 while (size > 0) {
189                         uint64_t io_sz = MIN(1024 * 1024, size);
190                         status = smb2_util_write(tree, *fh,
191                                                  buf + cur_off, cur_off, io_sz);
192                         torture_assert_ntstatus_ok(torture, status, "file write");
193
194                         size -= io_sz;
195                         cur_off += io_sz;
196                 }
197         }
198         return true;
199 }
200
201 static bool test_setup_copy_chunk(struct torture_context *torture,
202                                   struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
203                                   uint32_t nchunks,
204                                   struct smb2_handle *src_h,
205                                   uint64_t src_size,
206                                   uint32_t src_desired_access,
207                                   struct smb2_handle *dest_h,
208                                   uint64_t dest_size,
209                                   uint32_t dest_desired_access,
210                                   struct srv_copychunk_copy *cc_copy,
211                                   union smb_ioctl *ioctl)
212 {
213         struct req_resume_key_rsp res_key;
214         bool ok;
215         NTSTATUS status;
216         enum ndr_err_code ndr_ret;
217
218         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME,
219                                     src_h, src_size, src_desired_access,
220                                     FILE_ATTRIBUTE_NORMAL);
221         torture_assert(torture, ok, "src file create fill");
222
223         ok = test_setup_create_fill(torture, tree, mem_ctx, FNAME2,
224                                     dest_h, dest_size, dest_desired_access,
225                                     FILE_ATTRIBUTE_NORMAL);
226         torture_assert(torture, ok, "dest file create fill");
227
228         ZERO_STRUCTPN(ioctl);
229         ioctl->smb2.level = RAW_IOCTL_SMB2;
230         ioctl->smb2.in.file.handle = *src_h;
231         ioctl->smb2.in.function = FSCTL_SRV_REQUEST_RESUME_KEY;
232         /* Allow for Key + ContextLength + Context */
233         ioctl->smb2.in.max_response_size = 32;
234         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
235
236         status = smb2_ioctl(tree, mem_ctx, &ioctl->smb2);
237         torture_assert_ntstatus_ok(torture, status,
238                                    "FSCTL_SRV_REQUEST_RESUME_KEY");
239
240         ndr_ret = ndr_pull_struct_blob(&ioctl->smb2.out.out, mem_ctx, &res_key,
241                         (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
242
243         torture_assert_ndr_success(torture, ndr_ret,
244                                    "ndr_pull_req_resume_key_rsp");
245
246         ZERO_STRUCTPN(ioctl);
247         ioctl->smb2.level = RAW_IOCTL_SMB2;
248         ioctl->smb2.in.file.handle = *dest_h;
249         ioctl->smb2.in.function = FSCTL_SRV_COPYCHUNK;
250         ioctl->smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp);
251         ioctl->smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
252
253         ZERO_STRUCTPN(cc_copy);
254         memcpy(cc_copy->source_key, res_key.resume_key, ARRAY_SIZE(cc_copy->source_key));
255         cc_copy->chunk_count = nchunks;
256         cc_copy->chunks = talloc_zero_array(mem_ctx, struct srv_copychunk, nchunks);
257         torture_assert(torture, (cc_copy->chunks != NULL), "no memory for chunks");
258
259         return true;
260 }
261
262
263 static bool check_copy_chunk_rsp(struct torture_context *torture,
264                                  struct srv_copychunk_rsp *cc_rsp,
265                                  uint32_t ex_chunks_written,
266                                  uint32_t ex_chunk_bytes_written,
267                                  uint32_t ex_total_bytes_written)
268 {
269         torture_assert_int_equal(torture, cc_rsp->chunks_written,
270                                  ex_chunks_written, "num chunks");
271         torture_assert_int_equal(torture, cc_rsp->chunk_bytes_written,
272                                  ex_chunk_bytes_written, "chunk bytes written");
273         torture_assert_int_equal(torture, cc_rsp->total_bytes_written,
274                                  ex_total_bytes_written, "chunk total bytes");
275         return true;
276 }
277
278 static bool test_ioctl_copy_chunk_simple(struct torture_context *torture,
279                                          struct smb2_tree *tree)
280 {
281         struct smb2_handle src_h;
282         struct smb2_handle dest_h;
283         NTSTATUS status;
284         union smb_ioctl ioctl;
285         TALLOC_CTX *tmp_ctx = talloc_new(tree);
286         struct srv_copychunk_copy cc_copy;
287         struct srv_copychunk_rsp cc_rsp;
288         enum ndr_err_code ndr_ret;
289         bool ok;
290
291         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
292                                    1, /* 1 chunk */
293                                    &src_h, 4096, /* fill 4096 byte src file */
294                                    SEC_RIGHTS_FILE_ALL,
295                                    &dest_h, 0,  /* 0 byte dest file */
296                                    SEC_RIGHTS_FILE_ALL,
297                                    &cc_copy,
298                                    &ioctl);
299         if (!ok) {
300                 torture_fail(torture, "setup copy chunk error");
301         }
302
303         /* copy all src file data (via a single chunk desc) */
304         cc_copy.chunks[0].source_off = 0;
305         cc_copy.chunks[0].target_off = 0;
306         cc_copy.chunks[0].length = 4096;
307
308         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
309                                        &cc_copy,
310                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
311         torture_assert_ndr_success(torture, ndr_ret,
312                                    "ndr_push_srv_copychunk_copy");
313
314         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
315         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
316
317         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
318                                        &cc_rsp,
319                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
320         torture_assert_ndr_success(torture, ndr_ret,
321                                    "ndr_pull_srv_copychunk_rsp");
322
323         ok = check_copy_chunk_rsp(torture, &cc_rsp,
324                                   1,    /* chunks written */
325                                   0,    /* chunk bytes unsuccessfully written */
326                                   4096); /* total bytes written */
327         if (!ok) {
328                 torture_fail(torture, "bad copy chunk response data");
329         }
330
331         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
332         if (!ok) {
333                 torture_fail(torture, "inconsistent file data");
334         }
335
336         smb2_util_close(tree, src_h);
337         smb2_util_close(tree, dest_h);
338         talloc_free(tmp_ctx);
339         return true;
340 }
341
342 static bool test_ioctl_copy_chunk_multi(struct torture_context *torture,
343                                         struct smb2_tree *tree)
344 {
345         struct smb2_handle src_h;
346         struct smb2_handle dest_h;
347         NTSTATUS status;
348         union smb_ioctl ioctl;
349         TALLOC_CTX *tmp_ctx = talloc_new(tree);
350         struct srv_copychunk_copy cc_copy;
351         struct srv_copychunk_rsp cc_rsp;
352         enum ndr_err_code ndr_ret;
353         bool ok;
354
355         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
356                                    2, /* chunks */
357                                    &src_h, 8192, /* src file */
358                                    SEC_RIGHTS_FILE_ALL,
359                                    &dest_h, 0,  /* dest file */
360                                    SEC_RIGHTS_FILE_ALL,
361                                    &cc_copy,
362                                    &ioctl);
363         if (!ok) {
364                 torture_fail(torture, "setup copy chunk error");
365         }
366
367         /* copy all src file data via two chunks */
368         cc_copy.chunks[0].source_off = 0;
369         cc_copy.chunks[0].target_off = 0;
370         cc_copy.chunks[0].length = 4096;
371
372         cc_copy.chunks[1].source_off = 4096;
373         cc_copy.chunks[1].target_off = 4096;
374         cc_copy.chunks[1].length = 4096;
375
376         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
377                                        &cc_copy,
378                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
379         torture_assert_ndr_success(torture, ndr_ret,
380                                    "ndr_push_srv_copychunk_copy");
381
382         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
383         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
384
385         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
386                                        &cc_rsp,
387                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
388         torture_assert_ndr_success(torture, ndr_ret,
389                                    "ndr_pull_srv_copychunk_rsp");
390
391         ok = check_copy_chunk_rsp(torture, &cc_rsp,
392                                   2,    /* chunks written */
393                                   0,    /* chunk bytes unsuccessfully written */
394                                   8192);        /* total bytes written */
395         if (!ok) {
396                 torture_fail(torture, "bad copy chunk response data");
397         }
398
399         smb2_util_close(tree, src_h);
400         smb2_util_close(tree, dest_h);
401         talloc_free(tmp_ctx);
402         return true;
403 }
404
405 static bool test_ioctl_copy_chunk_tiny(struct torture_context *torture,
406                                        struct smb2_tree *tree)
407 {
408         struct smb2_handle src_h;
409         struct smb2_handle dest_h;
410         NTSTATUS status;
411         union smb_ioctl ioctl;
412         TALLOC_CTX *tmp_ctx = talloc_new(tree);
413         struct srv_copychunk_copy cc_copy;
414         struct srv_copychunk_rsp cc_rsp;
415         enum ndr_err_code ndr_ret;
416         bool ok;
417
418         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
419                                    2, /* chunks */
420                                    &src_h, 100, /* src file */
421                                    SEC_RIGHTS_FILE_ALL,
422                                    &dest_h, 0,  /* dest file */
423                                    SEC_RIGHTS_FILE_ALL,
424                                    &cc_copy,
425                                    &ioctl);
426         if (!ok) {
427                 torture_fail(torture, "setup copy chunk error");
428         }
429
430         /* copy all src file data via two chunks, sub block size chunks */
431         cc_copy.chunks[0].source_off = 0;
432         cc_copy.chunks[0].target_off = 0;
433         cc_copy.chunks[0].length = 50;
434
435         cc_copy.chunks[1].source_off = 50;
436         cc_copy.chunks[1].target_off = 50;
437         cc_copy.chunks[1].length = 50;
438
439         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
440                                        &cc_copy,
441                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
442         torture_assert_ndr_success(torture, ndr_ret,
443                                    "ndr_push_srv_copychunk_copy");
444
445         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
446         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
447
448         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
449                                        &cc_rsp,
450                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
451         torture_assert_ndr_success(torture, ndr_ret,
452                                    "ndr_pull_srv_copychunk_rsp");
453
454         ok = check_copy_chunk_rsp(torture, &cc_rsp,
455                                   2,    /* chunks written */
456                                   0,    /* chunk bytes unsuccessfully written */
457                                   100); /* total bytes written */
458         if (!ok) {
459                 torture_fail(torture, "bad copy chunk response data");
460         }
461
462         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 100, 0);
463         if (!ok) {
464                 torture_fail(torture, "inconsistent file data");
465         }
466
467         smb2_util_close(tree, src_h);
468         smb2_util_close(tree, dest_h);
469         talloc_free(tmp_ctx);
470         return true;
471 }
472
473 static bool test_ioctl_copy_chunk_over(struct torture_context *torture,
474                                        struct smb2_tree *tree)
475 {
476         struct smb2_handle src_h;
477         struct smb2_handle dest_h;
478         NTSTATUS status;
479         union smb_ioctl ioctl;
480         TALLOC_CTX *tmp_ctx = talloc_new(tree);
481         struct srv_copychunk_copy cc_copy;
482         struct srv_copychunk_rsp cc_rsp;
483         enum ndr_err_code ndr_ret;
484         bool ok;
485
486         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
487                                    2, /* chunks */
488                                    &src_h, 8192, /* src file */
489                                    SEC_RIGHTS_FILE_ALL,
490                                    &dest_h, 4096, /* dest file */
491                                    SEC_RIGHTS_FILE_ALL,
492                                    &cc_copy,
493                                    &ioctl);
494         if (!ok) {
495                 torture_fail(torture, "setup copy chunk error");
496         }
497
498         /* first chunk overwrites existing dest data */
499         cc_copy.chunks[0].source_off = 0;
500         cc_copy.chunks[0].target_off = 0;
501         cc_copy.chunks[0].length = 4096;
502
503         /* second chunk overwrites the first */
504         cc_copy.chunks[1].source_off = 4096;
505         cc_copy.chunks[1].target_off = 0;
506         cc_copy.chunks[1].length = 4096;
507
508         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
509                                        &cc_copy,
510                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
511         torture_assert_ndr_success(torture, ndr_ret,
512                                    "ndr_push_srv_copychunk_copy");
513
514         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
515         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
516
517         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
518                                        &cc_rsp,
519                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
520         torture_assert_ndr_success(torture, ndr_ret,
521                                    "ndr_pull_srv_copychunk_rsp");
522
523         ok = check_copy_chunk_rsp(torture, &cc_rsp,
524                                   2,    /* chunks written */
525                                   0,    /* chunk bytes unsuccessfully written */
526                                   8192); /* total bytes written */
527         if (!ok) {
528                 torture_fail(torture, "bad copy chunk response data");
529         }
530
531         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 4096);
532         if (!ok) {
533                 torture_fail(torture, "inconsistent file data");
534         }
535
536         smb2_util_close(tree, src_h);
537         smb2_util_close(tree, dest_h);
538         talloc_free(tmp_ctx);
539         return true;
540 }
541
542 static bool test_ioctl_copy_chunk_append(struct torture_context *torture,
543                                        struct smb2_tree *tree)
544 {
545         struct smb2_handle src_h;
546         struct smb2_handle dest_h;
547         NTSTATUS status;
548         union smb_ioctl ioctl;
549         TALLOC_CTX *tmp_ctx = talloc_new(tree);
550         struct srv_copychunk_copy cc_copy;
551         struct srv_copychunk_rsp cc_rsp;
552         enum ndr_err_code ndr_ret;
553         bool ok;
554
555         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
556                                    2, /* chunks */
557                                    &src_h, 4096, /* src file */
558                                    SEC_RIGHTS_FILE_ALL,
559                                    &dest_h, 0,  /* dest file */
560                                    SEC_RIGHTS_FILE_ALL,
561                                    &cc_copy,
562                                    &ioctl);
563         if (!ok) {
564                 torture_fail(torture, "setup copy chunk error");
565         }
566
567         cc_copy.chunks[0].source_off = 0;
568         cc_copy.chunks[0].target_off = 0;
569         cc_copy.chunks[0].length = 4096;
570
571         /* second chunk appends the same data to the first */
572         cc_copy.chunks[1].source_off = 0;
573         cc_copy.chunks[1].target_off = 4096;
574         cc_copy.chunks[1].length = 4096;
575
576         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
577                                        &cc_copy,
578                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
579         torture_assert_ndr_success(torture, ndr_ret,
580                                    "ndr_push_srv_copychunk_copy");
581
582         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
583         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
584
585         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
586                                        &cc_rsp,
587                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
588         torture_assert_ndr_success(torture, ndr_ret,
589                                    "ndr_pull_srv_copychunk_rsp");
590
591         ok = check_copy_chunk_rsp(torture, &cc_rsp,
592                                   2,    /* chunks written */
593                                   0,    /* chunk bytes unsuccessfully written */
594                                   8192); /* total bytes written */
595         if (!ok) {
596                 torture_fail(torture, "bad copy chunk response data");
597         }
598
599         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
600         if (!ok) {
601                 torture_fail(torture, "inconsistent file data");
602         }
603
604         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
605         if (!ok) {
606                 torture_fail(torture, "inconsistent file data");
607         }
608
609         smb2_util_close(tree, src_h);
610         smb2_util_close(tree, dest_h);
611         talloc_free(tmp_ctx);
612         return true;
613 }
614
615 static bool test_ioctl_copy_chunk_limits(struct torture_context *torture,
616                                          struct smb2_tree *tree)
617 {
618         struct smb2_handle src_h;
619         struct smb2_handle dest_h;
620         NTSTATUS status;
621         union smb_ioctl ioctl;
622         TALLOC_CTX *tmp_ctx = talloc_new(tree);
623         struct srv_copychunk_copy cc_copy;
624         struct srv_copychunk_rsp cc_rsp;
625         enum ndr_err_code ndr_ret;
626         bool ok;
627
628         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
629                                    1, /* chunks */
630                                    &src_h, 4096, /* src file */
631                                    SEC_RIGHTS_FILE_ALL,
632                                    &dest_h, 0,  /* dest file */
633                                    SEC_RIGHTS_FILE_ALL,
634                                    &cc_copy,
635                                    &ioctl);
636         if (!ok) {
637                 torture_fail(torture, "setup copy chunk error");
638         }
639
640         /* send huge chunk length request */
641         cc_copy.chunks[0].source_off = 0;
642         cc_copy.chunks[0].target_off = 0;
643         cc_copy.chunks[0].length = UINT_MAX;
644
645         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
646                                        &cc_copy,
647                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
648         torture_assert_ndr_success(torture, ndr_ret, "marshalling request");
649
650         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
651         torture_assert_ntstatus_equal(torture, status,
652                                       NT_STATUS_INVALID_PARAMETER,
653                                       "bad oversize chunk response");
654
655         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
656                                        &cc_rsp,
657                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
658         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
659
660         torture_comment(torture, "limit max chunks, got %u\n",
661                         cc_rsp.chunks_written);
662         torture_comment(torture, "limit max chunk len, got %u\n",
663                         cc_rsp.chunk_bytes_written);
664         torture_comment(torture, "limit max total bytes, got %u\n",
665                         cc_rsp.total_bytes_written);
666
667         smb2_util_close(tree, src_h);
668         smb2_util_close(tree, dest_h);
669         talloc_free(tmp_ctx);
670         return true;
671 }
672
673 static bool test_ioctl_copy_chunk_src_lck(struct torture_context *torture,
674                                           struct smb2_tree *tree)
675 {
676         struct smb2_handle src_h;
677         struct smb2_handle src_h2;
678         struct smb2_handle dest_h;
679         NTSTATUS status;
680         union smb_ioctl ioctl;
681         TALLOC_CTX *tmp_ctx = talloc_new(tree);
682         struct srv_copychunk_copy cc_copy;
683         struct srv_copychunk_rsp cc_rsp;
684         enum ndr_err_code ndr_ret;
685         bool ok;
686         struct smb2_lock lck;
687         struct smb2_lock_element el[1];
688
689         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
690                                    1, /* chunks */
691                                    &src_h, 4096, /* src file */
692                                    SEC_RIGHTS_FILE_ALL,
693                                    &dest_h, 0,  /* dest file */
694                                    SEC_RIGHTS_FILE_ALL,
695                                    &cc_copy,
696                                    &ioctl);
697         if (!ok) {
698                 torture_fail(torture, "setup copy chunk error");
699         }
700
701         cc_copy.chunks[0].source_off = 0;
702         cc_copy.chunks[0].target_off = 0;
703         cc_copy.chunks[0].length = 4096;
704
705         /* open and lock the copychunk src file */
706         status = torture_smb2_testfile(tree, FNAME, &src_h2);
707         torture_assert_ntstatus_ok(torture, status, "2nd src open");
708
709         lck.in.lock_count       = 0x0001;
710         lck.in.lock_sequence    = 0x00000000;
711         lck.in.file.handle      = src_h2;
712         lck.in.locks            = el;
713         el[0].offset            = cc_copy.chunks[0].source_off;
714         el[0].length            = cc_copy.chunks[0].length;
715         el[0].reserved          = 0;
716         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
717
718         status = smb2_lock(tree, &lck);
719         torture_assert_ntstatus_ok(torture, status, "lock");
720
721         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
722                                        &cc_copy,
723                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
724         torture_assert_ndr_success(torture, ndr_ret,
725                                    "ndr_push_srv_copychunk_copy");
726
727         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
728         /*
729          * 2k12 & Samba return lock_conflict, Windows 7 & 2k8 return success...
730          *
731          * Edgar Olougouna @ MS wrote:
732          * Regarding the FSCTL_SRV_COPYCHUNK and STATUS_FILE_LOCK_CONFLICT
733          * discrepancy observed between Windows versions, we confirm that the
734          * behavior change is expected.
735          *
736          * CopyChunk in Windows Server 2012 use regular Readfile/Writefile APIs
737          * to move the chunks from the source to the destination.
738          * These ReadFile/WriteFile APIs go through the byte-range lock checks,
739          * and this explains the observed STATUS_FILE_LOCK_CONFLICT error.
740          *
741          * Prior to Windows Server 2012, CopyChunk used mapped sections to move
742          * the data. And byte range locks are not enforced on mapped I/O, and
743          * this explains the STATUS_SUCCESS observed on Windows Server 2008 R2.
744          */
745         torture_assert_ntstatus_equal(torture, status,
746                                       NT_STATUS_FILE_LOCK_CONFLICT,
747                                       "FSCTL_SRV_COPYCHUNK locked");
748
749         /* should get cc response data with the lock conflict status */
750         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
751                                        &cc_rsp,
752                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
753         torture_assert_ndr_success(torture, ndr_ret,
754                                    "ndr_pull_srv_copychunk_rsp");
755         ok = check_copy_chunk_rsp(torture, &cc_rsp,
756                                   0,    /* chunks written */
757                                   0,    /* chunk bytes unsuccessfully written */
758                                   0);   /* total bytes written */
759
760         lck.in.lock_count       = 0x0001;
761         lck.in.lock_sequence    = 0x00000001;
762         lck.in.file.handle      = src_h2;
763         lck.in.locks            = el;
764         el[0].offset            = cc_copy.chunks[0].source_off;
765         el[0].length            = cc_copy.chunks[0].length;
766         el[0].reserved          = 0;
767         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
768         status = smb2_lock(tree, &lck);
769         torture_assert_ntstatus_ok(torture, status, "unlock");
770
771         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
772         torture_assert_ntstatus_ok(torture, status,
773                                    "FSCTL_SRV_COPYCHUNK unlocked");
774
775         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
776                                        &cc_rsp,
777                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
778         torture_assert_ndr_success(torture, ndr_ret,
779                                    "ndr_pull_srv_copychunk_rsp");
780
781         ok = check_copy_chunk_rsp(torture, &cc_rsp,
782                                   1,    /* chunks written */
783                                   0,    /* chunk bytes unsuccessfully written */
784                                   4096); /* total bytes written */
785         if (!ok) {
786                 torture_fail(torture, "bad copy chunk response data");
787         }
788
789         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
790         if (!ok) {
791                 torture_fail(torture, "inconsistent file data");
792         }
793
794         smb2_util_close(tree, src_h2);
795         smb2_util_close(tree, src_h);
796         smb2_util_close(tree, dest_h);
797         talloc_free(tmp_ctx);
798         return true;
799 }
800
801 static bool test_ioctl_copy_chunk_dest_lck(struct torture_context *torture,
802                                            struct smb2_tree *tree)
803 {
804         struct smb2_handle src_h;
805         struct smb2_handle dest_h;
806         struct smb2_handle dest_h2;
807         NTSTATUS status;
808         union smb_ioctl ioctl;
809         TALLOC_CTX *tmp_ctx = talloc_new(tree);
810         struct srv_copychunk_copy cc_copy;
811         struct srv_copychunk_rsp cc_rsp;
812         enum ndr_err_code ndr_ret;
813         bool ok;
814         struct smb2_lock lck;
815         struct smb2_lock_element el[1];
816
817         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
818                                    1, /* chunks */
819                                    &src_h, 4096, /* src file */
820                                    SEC_RIGHTS_FILE_ALL,
821                                    &dest_h, 4096,       /* dest file */
822                                    SEC_RIGHTS_FILE_ALL,
823                                    &cc_copy,
824                                    &ioctl);
825         if (!ok) {
826                 torture_fail(torture, "setup copy chunk error");
827         }
828
829         cc_copy.chunks[0].source_off = 0;
830         cc_copy.chunks[0].target_off = 0;
831         cc_copy.chunks[0].length = 4096;
832
833         /* open and lock the copychunk dest file */
834         status = torture_smb2_testfile(tree, FNAME2, &dest_h2);
835         torture_assert_ntstatus_ok(torture, status, "2nd src open");
836
837         lck.in.lock_count       = 0x0001;
838         lck.in.lock_sequence    = 0x00000000;
839         lck.in.file.handle      = dest_h2;
840         lck.in.locks            = el;
841         el[0].offset            = cc_copy.chunks[0].target_off;
842         el[0].length            = cc_copy.chunks[0].length;
843         el[0].reserved          = 0;
844         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
845
846         status = smb2_lock(tree, &lck);
847         torture_assert_ntstatus_ok(torture, status, "lock");
848
849         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
850                                        &cc_copy,
851                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
852         torture_assert_ndr_success(torture, ndr_ret,
853                                    "ndr_push_srv_copychunk_copy");
854
855         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
856         torture_assert_ntstatus_equal(torture, status,
857                                       NT_STATUS_FILE_LOCK_CONFLICT,
858                                       "FSCTL_SRV_COPYCHUNK locked");
859
860         lck.in.lock_count       = 0x0001;
861         lck.in.lock_sequence    = 0x00000001;
862         lck.in.file.handle      = dest_h2;
863         lck.in.locks            = el;
864         el[0].offset            = cc_copy.chunks[0].target_off;
865         el[0].length            = cc_copy.chunks[0].length;
866         el[0].reserved          = 0;
867         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
868         status = smb2_lock(tree, &lck);
869         torture_assert_ntstatus_ok(torture, status, "unlock");
870
871         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
872         torture_assert_ntstatus_ok(torture, status,
873                                    "FSCTL_SRV_COPYCHUNK unlocked");
874
875         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
876                                        &cc_rsp,
877                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
878         torture_assert_ndr_success(torture, ndr_ret,
879                                    "ndr_pull_srv_copychunk_rsp");
880
881         ok = check_copy_chunk_rsp(torture, &cc_rsp,
882                                   1,    /* chunks written */
883                                   0,    /* chunk bytes unsuccessfully written */
884                                   4096); /* total bytes written */
885         if (!ok) {
886                 torture_fail(torture, "bad copy chunk response data");
887         }
888
889         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
890         if (!ok) {
891                 torture_fail(torture, "inconsistent file data");
892         }
893
894         smb2_util_close(tree, dest_h2);
895         smb2_util_close(tree, src_h);
896         smb2_util_close(tree, dest_h);
897         talloc_free(tmp_ctx);
898         return true;
899 }
900
901 static bool test_ioctl_copy_chunk_bad_key(struct torture_context *torture,
902                                           struct smb2_tree *tree)
903 {
904         struct smb2_handle src_h;
905         struct smb2_handle dest_h;
906         NTSTATUS status;
907         union smb_ioctl ioctl;
908         TALLOC_CTX *tmp_ctx = talloc_new(tree);
909         struct srv_copychunk_copy cc_copy;
910         enum ndr_err_code ndr_ret;
911         bool ok;
912
913         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
914                                    1,
915                                    &src_h, 4096,
916                                    SEC_RIGHTS_FILE_ALL,
917                                    &dest_h, 0,
918                                    SEC_RIGHTS_FILE_ALL,
919                                    &cc_copy,
920                                    &ioctl);
921         if (!ok) {
922                 torture_fail(torture, "setup copy chunk error");
923         }
924
925         /* overwrite the resume key with a bogus value */
926         memcpy(cc_copy.source_key, "deadbeefdeadbeefdeadbeef", 24);
927
928         cc_copy.chunks[0].source_off = 0;
929         cc_copy.chunks[0].target_off = 0;
930         cc_copy.chunks[0].length = 4096;
931
932         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
933                                        &cc_copy,
934                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
935         torture_assert_ndr_success(torture, ndr_ret,
936                                    "ndr_push_srv_copychunk_copy");
937
938         /* Server 2k12 returns NT_STATUS_OBJECT_NAME_NOT_FOUND */
939         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
940         torture_assert_ntstatus_equal(torture, status,
941                                       NT_STATUS_OBJECT_NAME_NOT_FOUND,
942                                       "FSCTL_SRV_COPYCHUNK");
943
944         smb2_util_close(tree, src_h);
945         smb2_util_close(tree, dest_h);
946         talloc_free(tmp_ctx);
947         return true;
948 }
949
950 static bool test_ioctl_copy_chunk_src_is_dest(struct torture_context *torture,
951                                               struct smb2_tree *tree)
952 {
953         struct smb2_handle src_h;
954         struct smb2_handle dest_h;
955         NTSTATUS status;
956         union smb_ioctl ioctl;
957         TALLOC_CTX *tmp_ctx = talloc_new(tree);
958         struct srv_copychunk_copy cc_copy;
959         struct srv_copychunk_rsp cc_rsp;
960         enum ndr_err_code ndr_ret;
961         bool ok;
962
963         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
964                                    1,
965                                    &src_h, 8192,
966                                    SEC_RIGHTS_FILE_ALL,
967                                    &dest_h, 0,
968                                    SEC_RIGHTS_FILE_ALL,
969                                    &cc_copy,
970                                    &ioctl);
971         if (!ok) {
972                 torture_fail(torture, "setup copy chunk error");
973         }
974
975         /* the source is also the destination */
976         ioctl.smb2.in.file.handle = src_h;
977
978         /* non-overlapping */
979         cc_copy.chunks[0].source_off = 0;
980         cc_copy.chunks[0].target_off = 4096;
981         cc_copy.chunks[0].length = 4096;
982
983         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
984                                        &cc_copy,
985                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
986         torture_assert_ndr_success(torture, ndr_ret,
987                                    "ndr_push_srv_copychunk_copy");
988
989         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
990         torture_assert_ntstatus_ok(torture, status,
991                                    "FSCTL_SRV_COPYCHUNK");
992
993         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
994                                        &cc_rsp,
995                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
996         torture_assert_ndr_success(torture, ndr_ret,
997                                    "ndr_pull_srv_copychunk_rsp");
998
999         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1000                                   1,    /* chunks written */
1001                                   0,    /* chunk bytes unsuccessfully written */
1002                                   4096); /* total bytes written */
1003         if (!ok) {
1004                 torture_fail(torture, "bad copy chunk response data");
1005         }
1006
1007         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 4096, 0);
1008         if (!ok) {
1009                 torture_fail(torture, "inconsistent file data");
1010         }
1011         ok = check_pattern(torture, tree, tmp_ctx, src_h, 4096, 4096, 0);
1012         if (!ok) {
1013                 torture_fail(torture, "inconsistent file data");
1014         }
1015
1016         smb2_util_close(tree, src_h);
1017         smb2_util_close(tree, dest_h);
1018         talloc_free(tmp_ctx);
1019         return true;
1020 }
1021
1022 /*
1023  * Test a single-chunk copychunk request, where the source and target ranges
1024  * overlap, and the SourceKey refers to the same target file. E.g:
1025  *
1026  * Initial State
1027  * -------------
1028  *      File:           src_and_dest
1029  *      Offset:         0123456789
1030  *      Data:           abcdefghij
1031  *
1032  * Request
1033  * -------
1034  *      FSCTL_SRV_COPYCHUNK(src_and_dest)
1035  *      SourceKey = SRV_REQUEST_RESUME_KEY(src_and_dest)
1036  *      ChunkCount = 1
1037  *      Chunks[0].SourceOffset = 0
1038  *      Chunks[0].TargetOffset = 4
1039  *      Chunks[0].Length = 6
1040  *
1041  * Resultant State
1042  * ---------------
1043  *      File:           src_and_dest
1044  *      Offset:         0123456789
1045  *      Data:           abcdabcdef
1046  *
1047  * The resultant contents of src_and_dest is dependent on the server's
1048  * copy algorithm. In the above example, the server uses an IO buffer
1049  * large enough to hold the entire six-byte source data before writing
1050  * to TargetOffset. If the server were to use a four-byte IO buffer and
1051  * started reads/writes from the lowest offset, then the two overlapping
1052  * bytes in the above example would be overwritten before being read. The
1053  * resultant file contents would be abcdabcdab.
1054  *
1055  * Windows 2008r2 appears to use a 2048 byte copy buffer, overlapping bytes
1056  * after this offset are written before being read. Windows 2012 on the
1057  * other hand appears to use a buffer large enough to hold its maximum
1058  * supported chunk size (1M). Samba currently uses a 64k copy buffer by
1059  * default (vfs_cc_state.buf).
1060  *
1061  * This test uses an 8-byte overlap at 2040-2048, so that it passes against
1062  * Windows 2008, 2012 and Samba servers.
1063  */
1064 static bool
1065 test_ioctl_copy_chunk_src_is_dest_overlap(struct torture_context *torture,
1066                                           struct smb2_tree *tree)
1067 {
1068         struct smb2_handle src_h;
1069         struct smb2_handle dest_h;
1070         NTSTATUS status;
1071         union smb_ioctl ioctl;
1072         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1073         struct srv_copychunk_copy cc_copy;
1074         struct srv_copychunk_rsp cc_rsp;
1075         enum ndr_err_code ndr_ret;
1076         bool ok;
1077
1078         /* exceed the vfs_default copy buffer */
1079         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1080                                    1,
1081                                    &src_h, 2048 * 2,
1082                                    SEC_RIGHTS_FILE_ALL,
1083                                    &dest_h, 0,
1084                                    SEC_RIGHTS_FILE_ALL,
1085                                    &cc_copy,
1086                                    &ioctl);
1087         if (!ok) {
1088                 torture_fail(torture, "setup copy chunk error");
1089         }
1090
1091         /* the source is also the destination */
1092         ioctl.smb2.in.file.handle = src_h;
1093
1094         /* 8 bytes overlap between source and target ranges */
1095         cc_copy.chunks[0].source_off = 0;
1096         cc_copy.chunks[0].target_off = 2048 - 8;
1097         cc_copy.chunks[0].length = 2048;
1098
1099         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1100                                        &cc_copy,
1101                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1102         torture_assert_ndr_success(torture, ndr_ret,
1103                                    "ndr_push_srv_copychunk_copy");
1104
1105         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1106         torture_assert_ntstatus_ok(torture, status,
1107                                    "FSCTL_SRV_COPYCHUNK");
1108
1109         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1110                                        &cc_rsp,
1111                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1112         torture_assert_ndr_success(torture, ndr_ret,
1113                                    "ndr_pull_srv_copychunk_rsp");
1114
1115         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1116                                   1,    /* chunks written */
1117                                   0,    /* chunk bytes unsuccessfully written */
1118                                   2048); /* total bytes written */
1119         if (!ok) {
1120                 torture_fail(torture, "bad copy chunk response data");
1121         }
1122
1123         ok = check_pattern(torture, tree, tmp_ctx, src_h, 0, 2048 - 8, 0);
1124         if (!ok) {
1125                 torture_fail(torture, "inconsistent file data");
1126         }
1127         ok = check_pattern(torture, tree, tmp_ctx, src_h, 2048 - 8, 2048, 0);
1128         if (!ok) {
1129                 torture_fail(torture, "inconsistent file data");
1130         }
1131
1132         smb2_util_close(tree, src_h);
1133         smb2_util_close(tree, dest_h);
1134         talloc_free(tmp_ctx);
1135         return true;
1136 }
1137
1138 static bool test_ioctl_copy_chunk_bad_access(struct torture_context *torture,
1139                                              struct smb2_tree *tree)
1140 {
1141         struct smb2_handle src_h;
1142         struct smb2_handle dest_h;
1143         NTSTATUS status;
1144         union smb_ioctl ioctl;
1145         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1146         struct srv_copychunk_copy cc_copy;
1147         enum ndr_err_code ndr_ret;
1148         bool ok;
1149
1150         /* no read permission on src */
1151         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1152                                    1, /* 1 chunk */
1153                                    &src_h, 4096, /* fill 4096 byte src file */
1154                                    SEC_RIGHTS_FILE_WRITE,
1155                                    &dest_h, 0,  /* 0 byte dest file */
1156                                    SEC_RIGHTS_FILE_ALL,
1157                                    &cc_copy,
1158                                    &ioctl);
1159         if (!ok) {
1160                 torture_fail(torture, "setup copy chunk error");
1161         }
1162
1163         cc_copy.chunks[0].source_off = 0;
1164         cc_copy.chunks[0].target_off = 0;
1165         cc_copy.chunks[0].length = 4096;
1166
1167         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1168                                        &cc_copy,
1169                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1170         torture_assert_ndr_success(torture, ndr_ret,
1171                                    "ndr_push_srv_copychunk_copy");
1172
1173         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1174         torture_assert_ntstatus_equal(torture, status,
1175                                       NT_STATUS_ACCESS_DENIED,
1176                                       "FSCTL_SRV_COPYCHUNK");
1177
1178         smb2_util_close(tree, src_h);
1179         smb2_util_close(tree, dest_h);
1180
1181         /* no write permission on dest */
1182         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1183                                    1, /* 1 chunk */
1184                                    &src_h, 4096, /* fill 4096 byte src file */
1185                                    SEC_RIGHTS_FILE_ALL,
1186                                    &dest_h, 0,  /* 0 byte dest file */
1187                                    (SEC_RIGHTS_FILE_READ
1188                                     | SEC_RIGHTS_FILE_EXECUTE),
1189                                    &cc_copy,
1190                                    &ioctl);
1191         if (!ok) {
1192                 torture_fail(torture, "setup copy chunk error");
1193         }
1194
1195         cc_copy.chunks[0].source_off = 0;
1196         cc_copy.chunks[0].target_off = 0;
1197         cc_copy.chunks[0].length = 4096;
1198
1199         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1200                                        &cc_copy,
1201                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1202         torture_assert_ndr_success(torture, ndr_ret,
1203                                    "ndr_push_srv_copychunk_copy");
1204
1205         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1206         torture_assert_ntstatus_equal(torture, status,
1207                                       NT_STATUS_ACCESS_DENIED,
1208                                       "FSCTL_SRV_COPYCHUNK");
1209
1210         smb2_util_close(tree, src_h);
1211         smb2_util_close(tree, dest_h);
1212
1213         /* no read permission on dest */
1214         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1215                                    1, /* 1 chunk */
1216                                    &src_h, 4096, /* fill 4096 byte src file */
1217                                    SEC_RIGHTS_FILE_ALL,
1218                                    &dest_h, 0,  /* 0 byte dest file */
1219                                    (SEC_RIGHTS_FILE_WRITE
1220                                     | SEC_RIGHTS_FILE_EXECUTE),
1221                                    &cc_copy,
1222                                    &ioctl);
1223         if (!ok) {
1224                 torture_fail(torture, "setup copy chunk error");
1225         }
1226
1227         cc_copy.chunks[0].source_off = 0;
1228         cc_copy.chunks[0].target_off = 0;
1229         cc_copy.chunks[0].length = 4096;
1230
1231         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1232                                        &cc_copy,
1233                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1234         torture_assert_ndr_success(torture, ndr_ret,
1235                                    "ndr_push_srv_copychunk_copy");
1236
1237         /*
1238          * FSCTL_SRV_COPYCHUNK requires read permission on dest,
1239          * FSCTL_SRV_COPYCHUNK_WRITE (not supported by Samba) on the other hand
1240          * does not.
1241          */
1242         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1243         torture_assert_ntstatus_equal(torture, status,
1244                                       NT_STATUS_ACCESS_DENIED,
1245                                       "FSCTL_SRV_COPYCHUNK");
1246
1247         smb2_util_close(tree, src_h);
1248         smb2_util_close(tree, dest_h);
1249         talloc_free(tmp_ctx);
1250
1251         return true;
1252 }
1253
1254 static bool test_ioctl_copy_chunk_src_exceed(struct torture_context *torture,
1255                                              struct smb2_tree *tree)
1256 {
1257         struct smb2_handle src_h;
1258         struct smb2_handle dest_h;
1259         NTSTATUS status;
1260         union smb_ioctl ioctl;
1261         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1262         struct srv_copychunk_copy cc_copy;
1263         struct srv_copychunk_rsp cc_rsp;
1264         enum ndr_err_code ndr_ret;
1265         bool ok;
1266
1267         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1268                                    1, /* 1 chunk */
1269                                    &src_h, 4096, /* fill 4096 byte src file */
1270                                    SEC_RIGHTS_FILE_ALL,
1271                                    &dest_h, 0,  /* 0 byte dest file */
1272                                    SEC_RIGHTS_FILE_ALL,
1273                                    &cc_copy,
1274                                    &ioctl);
1275         if (!ok) {
1276                 torture_fail(torture, "setup copy chunk error");
1277         }
1278
1279         /* Request copy where off + length exceeds size of src */
1280         cc_copy.chunks[0].source_off = 1024;
1281         cc_copy.chunks[0].target_off = 0;
1282         cc_copy.chunks[0].length = 4096;
1283
1284         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1285                                        &cc_copy,
1286                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1287         torture_assert_ndr_success(torture, ndr_ret,
1288                                    "ndr_push_srv_copychunk_copy");
1289
1290         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1291         torture_assert_ntstatus_equal(torture, status,
1292                                       NT_STATUS_INVALID_VIEW_SIZE,
1293                                       "FSCTL_SRV_COPYCHUNK oversize");
1294
1295         /* Request copy where length exceeds size of src */
1296         cc_copy.chunks[0].source_off = 1024;
1297         cc_copy.chunks[0].target_off = 0;
1298         cc_copy.chunks[0].length = 3072;
1299
1300         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1301                                        &cc_copy,
1302                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1303         torture_assert_ndr_success(torture, ndr_ret,
1304                                    "ndr_push_srv_copychunk_copy");
1305
1306         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1307         torture_assert_ntstatus_ok(torture, status,
1308                                    "FSCTL_SRV_COPYCHUNK just right");
1309
1310         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1311                                        &cc_rsp,
1312                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1313         torture_assert_ndr_success(torture, ndr_ret,
1314                                    "ndr_pull_srv_copychunk_rsp");
1315
1316         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1317                                   1,    /* chunks written */
1318                                   0,    /* chunk bytes unsuccessfully written */
1319                                   3072); /* total bytes written */
1320         if (!ok) {
1321                 torture_fail(torture, "bad copy chunk response data");
1322         }
1323
1324         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 3072, 1024);
1325         if (!ok) {
1326                 torture_fail(torture, "inconsistent file data");
1327         }
1328
1329         smb2_util_close(tree, src_h);
1330         smb2_util_close(tree, dest_h);
1331         talloc_free(tmp_ctx);
1332         return true;
1333 }
1334
1335 static bool
1336 test_ioctl_copy_chunk_src_exceed_multi(struct torture_context *torture,
1337                                        struct smb2_tree *tree)
1338 {
1339         struct smb2_handle src_h;
1340         struct smb2_handle dest_h;
1341         NTSTATUS status;
1342         union smb_ioctl ioctl;
1343         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1344         struct srv_copychunk_copy cc_copy;
1345         struct srv_copychunk_rsp cc_rsp;
1346         enum ndr_err_code ndr_ret;
1347         bool ok;
1348
1349         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1350                                    2, /* 2 chunks */
1351                                    &src_h, 8192, /* fill 8192 byte src file */
1352                                    SEC_RIGHTS_FILE_ALL,
1353                                    &dest_h, 0,  /* 0 byte dest file */
1354                                    SEC_RIGHTS_FILE_ALL,
1355                                    &cc_copy,
1356                                    &ioctl);
1357         if (!ok) {
1358                 torture_fail(torture, "setup copy chunk error");
1359         }
1360
1361         /* Request copy where off + length exceeds size of src */
1362         cc_copy.chunks[0].source_off = 0;
1363         cc_copy.chunks[0].target_off = 0;
1364         cc_copy.chunks[0].length = 4096;
1365
1366         cc_copy.chunks[1].source_off = 4096;
1367         cc_copy.chunks[1].target_off = 4096;
1368         cc_copy.chunks[1].length = 8192;
1369
1370         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1371                                        &cc_copy,
1372                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1373         torture_assert_ndr_success(torture, ndr_ret,
1374                                    "ndr_push_srv_copychunk_copy");
1375
1376         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1377         torture_assert_ntstatus_equal(torture, status,
1378                                       NT_STATUS_INVALID_VIEW_SIZE,
1379                                       "FSCTL_SRV_COPYCHUNK oversize");
1380         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1381                                        &cc_rsp,
1382                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1383         torture_assert_ndr_success(torture, ndr_ret, "unmarshalling response");
1384
1385         /* first chunk should still be written */
1386         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1387                                   1,    /* chunks written */
1388                                   0,    /* chunk bytes unsuccessfully written */
1389                                   4096); /* total bytes written */
1390         if (!ok) {
1391                 torture_fail(torture, "bad copy chunk response data");
1392         }
1393         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 0, 4096, 0);
1394         if (!ok) {
1395                 torture_fail(torture, "inconsistent file data");
1396         }
1397
1398         smb2_util_close(tree, src_h);
1399         smb2_util_close(tree, dest_h);
1400         talloc_free(tmp_ctx);
1401         return true;
1402 }
1403
1404 static bool test_ioctl_copy_chunk_sparse_dest(struct torture_context *torture,
1405                                               struct smb2_tree *tree)
1406 {
1407         struct smb2_handle src_h;
1408         struct smb2_handle dest_h;
1409         NTSTATUS status;
1410         union smb_ioctl ioctl;
1411         struct smb2_read r;
1412         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1413         struct srv_copychunk_copy cc_copy;
1414         struct srv_copychunk_rsp cc_rsp;
1415         enum ndr_err_code ndr_ret;
1416         bool ok;
1417         int i;
1418
1419         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1420                                    1, /* 1 chunk */
1421                                    &src_h, 4096, /* fill 4096 byte src file */
1422                                    SEC_RIGHTS_FILE_ALL,
1423                                    &dest_h, 0,  /* 0 byte dest file */
1424                                    SEC_RIGHTS_FILE_ALL,
1425                                    &cc_copy,
1426                                    &ioctl);
1427         if (!ok) {
1428                 torture_fail(torture, "setup copy chunk error");
1429         }
1430
1431         /* copy all src file data (via a single chunk desc) */
1432         cc_copy.chunks[0].source_off = 0;
1433         cc_copy.chunks[0].target_off = 4096;
1434         cc_copy.chunks[0].length = 4096;
1435
1436         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1437                                        &cc_copy,
1438                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1439         torture_assert_ndr_success(torture, ndr_ret,
1440                                    "ndr_push_srv_copychunk_copy");
1441
1442         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1443         torture_assert_ntstatus_ok(torture, status, "FSCTL_SRV_COPYCHUNK");
1444
1445         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
1446                                        &cc_rsp,
1447                         (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
1448         torture_assert_ndr_success(torture, ndr_ret,
1449                                    "ndr_pull_srv_copychunk_rsp");
1450
1451         ok = check_copy_chunk_rsp(torture, &cc_rsp,
1452                                   1,    /* chunks written */
1453                                   0,    /* chunk bytes unsuccessfully written */
1454                                   4096); /* total bytes written */
1455         if (!ok) {
1456                 torture_fail(torture, "bad copy chunk response data");
1457         }
1458
1459         /* check for zeros in first 4k */
1460         ZERO_STRUCT(r);
1461         r.in.file.handle = dest_h;
1462         r.in.length      = 4096;
1463         r.in.offset      = 0;
1464         status = smb2_read(tree, tmp_ctx, &r);
1465         torture_assert_ntstatus_ok(torture, status, "read");
1466
1467         torture_assert_u64_equal(torture, r.out.data.length, 4096,
1468                                  "read data len mismatch");
1469
1470         for (i = 0; i < 4096; i++) {
1471                 torture_assert(torture, (r.out.data.data[i] == 0),
1472                                "sparse did not pass class");
1473         }
1474
1475         ok = check_pattern(torture, tree, tmp_ctx, dest_h, 4096, 4096, 0);
1476         if (!ok) {
1477                 torture_fail(torture, "inconsistent file data");
1478         }
1479
1480         smb2_util_close(tree, src_h);
1481         smb2_util_close(tree, dest_h);
1482         talloc_free(tmp_ctx);
1483         return true;
1484 }
1485
1486 /*
1487  * set the ioctl MaxOutputResponse size to less than
1488  * sizeof(struct srv_copychunk_rsp)
1489  */
1490 static bool test_ioctl_copy_chunk_max_output_sz(struct torture_context *torture,
1491                                                 struct smb2_tree *tree)
1492 {
1493         struct smb2_handle src_h;
1494         struct smb2_handle dest_h;
1495         NTSTATUS status;
1496         union smb_ioctl ioctl;
1497         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1498         struct srv_copychunk_copy cc_copy;
1499         enum ndr_err_code ndr_ret;
1500         bool ok;
1501
1502         ok = test_setup_copy_chunk(torture, tree, tmp_ctx,
1503                                    1, /* 1 chunk */
1504                                    &src_h, 4096, /* fill 4096 byte src file */
1505                                    SEC_RIGHTS_FILE_ALL,
1506                                    &dest_h, 0,  /* 0 byte dest file */
1507                                    SEC_RIGHTS_FILE_ALL,
1508                                    &cc_copy,
1509                                    &ioctl);
1510         if (!ok) {
1511                 torture_fail(torture, "setup copy chunk error");
1512         }
1513
1514         cc_copy.chunks[0].source_off = 0;
1515         cc_copy.chunks[0].target_off = 0;
1516         cc_copy.chunks[0].length = 4096;
1517         /* req is valid, but use undersize max_response_size */
1518         ioctl.smb2.in.max_response_size = sizeof(struct srv_copychunk_rsp) - 1;
1519
1520         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
1521                                        &cc_copy,
1522                         (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
1523         torture_assert_ndr_success(torture, ndr_ret,
1524                                    "ndr_push_srv_copychunk_copy");
1525
1526         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1527         torture_assert_ntstatus_equal(torture, status,
1528                                       NT_STATUS_INVALID_PARAMETER,
1529                                       "FSCTL_SRV_COPYCHUNK");
1530
1531         smb2_util_close(tree, src_h);
1532         smb2_util_close(tree, dest_h);
1533         talloc_free(tmp_ctx);
1534         return true;
1535 }
1536
1537 static NTSTATUS test_ioctl_compress_fs_supported(struct torture_context *torture,
1538                                                  struct smb2_tree *tree,
1539                                                  TALLOC_CTX *mem_ctx,
1540                                                  struct smb2_handle *fh,
1541                                                  bool *compress_support)
1542 {
1543         NTSTATUS status;
1544         union smb_fsinfo info;
1545
1546         ZERO_STRUCT(info);
1547         info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION;
1548         info.generic.handle = *fh;
1549         status = smb2_getinfo_fs(tree, tree, &info);
1550         if (!NT_STATUS_IS_OK(status)) {
1551                 return status;
1552         }
1553
1554         if (info.attribute_info.out.fs_attr & FILE_FILE_COMPRESSION) {
1555                 *compress_support = true;
1556         } else {
1557                 *compress_support = false;
1558         }
1559         return NT_STATUS_OK;
1560 }
1561
1562 static NTSTATUS test_ioctl_compress_get(struct torture_context *torture,
1563                                         TALLOC_CTX *mem_ctx,
1564                                         struct smb2_tree *tree,
1565                                         struct smb2_handle fh,
1566                                         uint16_t *_compression_fmt)
1567 {
1568         union smb_ioctl ioctl;
1569         struct compression_state cmpr_state;
1570         enum ndr_err_code ndr_ret;
1571         NTSTATUS status;
1572
1573         ZERO_STRUCT(ioctl);
1574         ioctl.smb2.level = RAW_IOCTL_SMB2;
1575         ioctl.smb2.in.file.handle = fh;
1576         ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1577         ioctl.smb2.in.max_response_size = sizeof(struct compression_state);
1578         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1579
1580         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1581         if (!NT_STATUS_IS_OK(status)) {
1582                 return status;
1583         }
1584
1585         ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, mem_ctx,
1586                                        &cmpr_state,
1587                         (ndr_pull_flags_fn_t)ndr_pull_compression_state);
1588
1589         if (ndr_ret != NDR_ERR_SUCCESS) {
1590                 return NT_STATUS_INTERNAL_ERROR;
1591         }
1592
1593         *_compression_fmt = cmpr_state.format;
1594         return NT_STATUS_OK;
1595 }
1596
1597 static NTSTATUS test_ioctl_compress_set(struct torture_context *torture,
1598                                         TALLOC_CTX *mem_ctx,
1599                                         struct smb2_tree *tree,
1600                                         struct smb2_handle fh,
1601                                         uint16_t compression_fmt)
1602 {
1603         union smb_ioctl ioctl;
1604         struct compression_state cmpr_state;
1605         enum ndr_err_code ndr_ret;
1606         NTSTATUS status;
1607
1608         ZERO_STRUCT(ioctl);
1609         ioctl.smb2.level = RAW_IOCTL_SMB2;
1610         ioctl.smb2.in.file.handle = fh;
1611         ioctl.smb2.in.function = FSCTL_SET_COMPRESSION;
1612         ioctl.smb2.in.max_response_size = 0;
1613         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1614
1615         cmpr_state.format = compression_fmt;
1616         ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, mem_ctx,
1617                                        &cmpr_state,
1618                         (ndr_push_flags_fn_t)ndr_push_compression_state);
1619         if (ndr_ret != NDR_ERR_SUCCESS) {
1620                 return NT_STATUS_INTERNAL_ERROR;
1621         }
1622
1623         status = smb2_ioctl(tree, mem_ctx, &ioctl.smb2);
1624         return status;
1625 }
1626
1627 static bool test_ioctl_compress_file_flag(struct torture_context *torture,
1628                                             struct smb2_tree *tree)
1629 {
1630         struct smb2_handle fh;
1631         NTSTATUS status;
1632         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1633         bool ok;
1634         uint16_t compression_fmt;
1635
1636         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1637                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1638                                     FILE_ATTRIBUTE_NORMAL);
1639         torture_assert(torture, ok, "setup compression file");
1640
1641         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1642                                                   &ok);
1643         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1644         if (!ok) {
1645                 smb2_util_close(tree, fh);
1646                 torture_skip(torture, "FS compression not supported\n");
1647         }
1648
1649         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1650                                          &compression_fmt);
1651         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1652
1653         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1654                        "initial compression state not NONE");
1655
1656         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1657                                          COMPRESSION_FORMAT_DEFAULT);
1658         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1659
1660         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1661                                          &compression_fmt);
1662         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1663
1664         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1665                        "invalid compression state after set");
1666
1667         smb2_util_close(tree, fh);
1668         talloc_free(tmp_ctx);
1669         return true;
1670 }
1671
1672 static bool test_ioctl_compress_dir_inherit(struct torture_context *torture,
1673                                             struct smb2_tree *tree)
1674 {
1675         struct smb2_handle dirh;
1676         struct smb2_handle fh;
1677         NTSTATUS status;
1678         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1679         uint16_t compression_fmt;
1680         bool ok;
1681         char path_buf[PATH_MAX];
1682
1683         smb2_deltree(tree, DNAME);
1684         status = smb2_util_mkdir(tree, DNAME);
1685         torture_assert_ntstatus_ok(torture, status, "mkdir");
1686
1687         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1688                                     DNAME, &dirh, 0, SEC_RIGHTS_FILE_ALL,
1689                                     FILE_ATTRIBUTE_DIRECTORY);
1690         torture_assert(torture, ok, "setup compression directory");
1691
1692         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &dirh,
1693                                                   &ok);
1694         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1695         if (!ok) {
1696                 smb2_util_close(tree, dirh);
1697                 smb2_deltree(tree, DNAME);
1698                 torture_skip(torture, "FS compression not supported\n");
1699         }
1700
1701         /* set compression on base share, then check for file inheritance */
1702         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1703                                          COMPRESSION_FORMAT_LZNT1);
1704         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1705
1706         status = test_ioctl_compress_get(torture, tmp_ctx, tree, dirh,
1707                                          &compression_fmt);
1708         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1709
1710         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1711                        "invalid compression state after set");
1712
1713         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME);
1714         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1715                                     path_buf, &fh, 4096, SEC_RIGHTS_FILE_ALL,
1716                                     FILE_ATTRIBUTE_NORMAL);
1717         torture_assert(torture, ok, "setup compression file");
1718
1719         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1720                                          &compression_fmt);
1721         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1722
1723         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1724                        "compression attr not inherited by new file");
1725
1726         /* check compressed data is consistent */
1727         ok = check_pattern(torture, tree, tmp_ctx, fh, 0, 4096, 0);
1728
1729         /* disable dir compression attr, file should remain compressed */
1730         status = test_ioctl_compress_set(torture, tmp_ctx, tree, dirh,
1731                                          COMPRESSION_FORMAT_NONE);
1732         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1733
1734         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1735                                          &compression_fmt);
1736         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1737
1738         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_LZNT1),
1739                        "file compression attr removed after dir change");
1740         smb2_util_close(tree, fh);
1741
1742         /* new files should no longer inherit compression attr */
1743         snprintf(path_buf, PATH_MAX, "%s\\%s", DNAME, FNAME2);
1744         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1745                                     path_buf, &fh, 0, SEC_RIGHTS_FILE_ALL,
1746                                     FILE_ATTRIBUTE_NORMAL);
1747         torture_assert(torture, ok, "setup file");
1748
1749         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1750                                          &compression_fmt);
1751         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1752
1753         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1754                        "compression attr present on new file");
1755
1756         smb2_util_close(tree, fh);
1757         smb2_util_close(tree, dirh);
1758         smb2_deltree(tree, DNAME);
1759         talloc_free(tmp_ctx);
1760         return true;
1761 }
1762
1763 static bool test_ioctl_compress_invalid_format(struct torture_context *torture,
1764                                                struct smb2_tree *tree)
1765 {
1766         struct smb2_handle fh;
1767         NTSTATUS status;
1768         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1769         bool ok;
1770         uint16_t compression_fmt;
1771
1772         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1773                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1774                                     FILE_ATTRIBUTE_NORMAL);
1775         torture_assert(torture, ok, "setup compression file");
1776
1777         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1778                                                   &ok);
1779         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1780         if (!ok) {
1781                 smb2_util_close(tree, fh);
1782                 torture_skip(torture, "FS compression not supported\n");
1783         }
1784
1785         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1786                                          0x0042); /* bogus */
1787         torture_assert_ntstatus_equal(torture, status,
1788                                       NT_STATUS_INVALID_PARAMETER,
1789                                       "invalid FSCTL_SET_COMPRESSION");
1790
1791         status = test_ioctl_compress_get(torture, tmp_ctx, tree, fh,
1792                                          &compression_fmt);
1793         torture_assert_ntstatus_ok(torture, status, "FSCTL_GET_COMPRESSION");
1794
1795         torture_assert(torture, (compression_fmt == COMPRESSION_FORMAT_NONE),
1796                        "initial compression state not NONE");
1797
1798         smb2_util_close(tree, fh);
1799         talloc_free(tmp_ctx);
1800         return true;
1801 }
1802
1803 static bool test_ioctl_compress_invalid_buf(struct torture_context *torture,
1804                                             struct smb2_tree *tree)
1805 {
1806         struct smb2_handle fh;
1807         NTSTATUS status;
1808         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1809         bool ok;
1810         union smb_ioctl ioctl;
1811
1812         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1813                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1814                                     FILE_ATTRIBUTE_NORMAL);
1815         torture_assert(torture, ok, "setup compression file");
1816
1817         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1818                                                   &ok);
1819         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1820         if (!ok) {
1821                 smb2_util_close(tree, fh);
1822                 torture_skip(torture, "FS compression not supported\n");
1823         }
1824
1825         ZERO_STRUCT(ioctl);
1826         ioctl.smb2.level = RAW_IOCTL_SMB2;
1827         ioctl.smb2.in.file.handle = fh;
1828         ioctl.smb2.in.function = FSCTL_GET_COMPRESSION;
1829         ioctl.smb2.in.max_response_size = 0;    /* no room for rsp data */
1830         ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
1831
1832         status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
1833         /* expect Server 2k12 response status */
1834         torture_assert_ntstatus_equal(torture, status,
1835                                       NT_STATUS_INVALID_USER_BUFFER,
1836                                       "invalid FSCTL_SET_COMPRESSION");
1837
1838         smb2_util_close(tree, fh);
1839         talloc_free(tmp_ctx);
1840         return true;
1841 }
1842
1843 static bool test_ioctl_compress_query_file_attr(struct torture_context *torture,
1844                                                 struct smb2_tree *tree)
1845 {
1846         struct smb2_handle fh;
1847         union smb_fileinfo io;
1848         NTSTATUS status;
1849         TALLOC_CTX *tmp_ctx = talloc_new(tree);
1850         bool ok;
1851
1852         ok = test_setup_create_fill(torture, tree, tmp_ctx,
1853                                     FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
1854                                     FILE_ATTRIBUTE_NORMAL);
1855         torture_assert(torture, ok, "setup compression file");
1856
1857         status = test_ioctl_compress_fs_supported(torture, tree, tmp_ctx, &fh,
1858                                                   &ok);
1859         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
1860         if (!ok) {
1861                 smb2_util_close(tree, fh);
1862                 torture_skip(torture, "FS compression not supported\n");
1863         }
1864
1865         status = smb2_getinfo_file(tree, tmp_ctx, &io);
1866         ZERO_STRUCT(io);
1867         io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
1868         io.generic.in.file.handle = fh;
1869         status = smb2_getinfo_file(tree, tmp_ctx, &io);
1870         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
1871
1872         torture_assert(torture,
1873                 ((io.all_info2.out.attrib & FILE_ATTRIBUTE_COMPRESSED) == 0),
1874                        "compression attr before set");
1875
1876         status = test_ioctl_compress_set(torture, tmp_ctx, tree, fh,
1877                                          COMPRESSION_FORMAT_DEFAULT);
1878         torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_COMPRESSION");
1879
1880         ZERO_STRUCT(io);
1881         io.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
1882         io.generic.in.file.handle = fh;
1883         status = smb2_getinfo_file(tree, tmp_ctx, &io);
1884         torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FILE");
1885
1886         torture_assert(torture,
1887                        (io.basic_info.out.attrib & FILE_ATTRIBUTE_COMPRESSED),
1888                        "no compression attr after set");
1889
1890         smb2_util_close(tree, fh);
1891         talloc_free(tmp_ctx);
1892         return true;
1893 }
1894
1895 /*
1896    basic testing of SMB2 ioctls
1897 */
1898 struct torture_suite *torture_smb2_ioctl_init(void)
1899 {
1900         struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "ioctl");
1901
1902         torture_suite_add_1smb2_test(suite, "shadow_copy",
1903                                      test_ioctl_get_shadow_copy);
1904         torture_suite_add_1smb2_test(suite, "req_resume_key",
1905                                      test_ioctl_req_resume_key);
1906         torture_suite_add_1smb2_test(suite, "copy_chunk_simple",
1907                                      test_ioctl_copy_chunk_simple);
1908         torture_suite_add_1smb2_test(suite, "copy_chunk_multi",
1909                                      test_ioctl_copy_chunk_multi);
1910         torture_suite_add_1smb2_test(suite, "copy_chunk_tiny",
1911                                      test_ioctl_copy_chunk_tiny);
1912         torture_suite_add_1smb2_test(suite, "copy_chunk_overwrite",
1913                                      test_ioctl_copy_chunk_over);
1914         torture_suite_add_1smb2_test(suite, "copy_chunk_append",
1915                                      test_ioctl_copy_chunk_append);
1916         torture_suite_add_1smb2_test(suite, "copy_chunk_limits",
1917                                      test_ioctl_copy_chunk_limits);
1918         torture_suite_add_1smb2_test(suite, "copy_chunk_src_lock",
1919                                      test_ioctl_copy_chunk_src_lck);
1920         torture_suite_add_1smb2_test(suite, "copy_chunk_dest_lock",
1921                                      test_ioctl_copy_chunk_dest_lck);
1922         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_key",
1923                                      test_ioctl_copy_chunk_bad_key);
1924         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest",
1925                                      test_ioctl_copy_chunk_src_is_dest);
1926         torture_suite_add_1smb2_test(suite, "copy_chunk_src_is_dest_overlap",
1927                                      test_ioctl_copy_chunk_src_is_dest_overlap);
1928         torture_suite_add_1smb2_test(suite, "copy_chunk_bad_access",
1929                                      test_ioctl_copy_chunk_bad_access);
1930         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed",
1931                                      test_ioctl_copy_chunk_src_exceed);
1932         torture_suite_add_1smb2_test(suite, "copy_chunk_src_exceed_multi",
1933                                      test_ioctl_copy_chunk_src_exceed_multi);
1934         torture_suite_add_1smb2_test(suite, "copy_chunk_sparse_dest",
1935                                      test_ioctl_copy_chunk_sparse_dest);
1936         torture_suite_add_1smb2_test(suite, "copy_chunk_max_output_sz",
1937                                      test_ioctl_copy_chunk_max_output_sz);
1938         torture_suite_add_1smb2_test(suite, "compress_file_flag",
1939                                      test_ioctl_compress_file_flag);
1940         torture_suite_add_1smb2_test(suite, "compress_dir_inherit",
1941                                      test_ioctl_compress_dir_inherit);
1942         torture_suite_add_1smb2_test(suite, "compress_invalid_format",
1943                                      test_ioctl_compress_invalid_format);
1944         torture_suite_add_1smb2_test(suite, "compress_invalid_buf",
1945                                      test_ioctl_compress_invalid_buf);
1946         torture_suite_add_1smb2_test(suite, "compress_query_file_attr",
1947                                      test_ioctl_compress_query_file_attr);
1948
1949         suite->description = talloc_strdup(suite, "SMB2-IOCTL tests");
1950
1951         return suite;
1952 }
1953