LCOV - code coverage report
Current view: top level - source4/torture/smb2 - ioctl.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 2279 3068 74.3 %
Date: 2021-09-23 10:06:22 Functions: 95 101 94.1 %

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

Generated by: LCOV version 1.13