LCOV - code coverage report
Current view: top level - source4/torture/smb2 - ioctl.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 2415 3730 64.7 %
Date: 2024-02-28 12:06:22 Functions: 98 98 100.0 %

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

Generated by: LCOV version 1.14