LCOV - code coverage report
Current view: top level - source3/smbd - smb2_ioctl_filesys.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 139 317 43.8 %
Date: 2021-09-23 10:06:22 Functions: 7 19 36.8 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Core SMB2 server
       4             : 
       5             :    Copyright (C) Stefan Metzmacher 2009
       6             :    Copyright (C) David Disseldorp 2013-2015
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             : 
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "smbd/smbd.h"
      24             : #include "smbd/globals.h"
      25             : #include "../libcli/smb/smb_common.h"
      26             : #include "../libcli/security/security.h"
      27             : #include "../lib/util/tevent_ntstatus.h"
      28             : #include "rpc_server/srv_pipe_hnd.h"
      29             : #include "include/ntioctl.h"
      30             : #include "../librpc/ndr/libndr.h"
      31             : #include "librpc/gen_ndr/ndr_ioctl.h"
      32             : #include "smb2_ioctl_private.h"
      33             : #include "lib/util/sys_rw.h"
      34             : 
      35             : #undef DBGC_CLASS
      36             : #define DBGC_CLASS DBGC_SMB2
      37             : 
      38             : /*
      39             :  * XXX this may reduce dup_extents->byte_count so that it's less than the
      40             :  * target file size.
      41             :  */
      42           0 : static NTSTATUS fsctl_dup_extents_check_lengths(struct files_struct *src_fsp,
      43             :                                                 struct files_struct *dst_fsp,
      44             :                                 struct fsctl_dup_extents_to_file *dup_extents)
      45             : {
      46             :         NTSTATUS status;
      47             : 
      48           0 :         if ((dup_extents->source_off + dup_extents->byte_count
      49           0 :                                                 < dup_extents->source_off)
      50           0 :          || (dup_extents->target_off + dup_extents->byte_count
      51           0 :                                                 < dup_extents->target_off)) {
      52           0 :                 return NT_STATUS_INVALID_PARAMETER;     /* wrap */
      53             :         }
      54             : 
      55           0 :         status = vfs_stat_fsp(src_fsp);
      56           0 :         if (!NT_STATUS_IS_OK(status)) {
      57           0 :                 return status;
      58             :         }
      59             : 
      60             :         /*
      61             :          * XXX vfs_btrfs and vfs_default have size checks in the copychunk
      62             :          * handler, as this needs to be rechecked after the src has potentially
      63             :          * been extended by a previous chunk in the compound copychunk req.
      64             :          */
      65           0 :         if (src_fsp->fsp_name->st.st_ex_size
      66           0 :                         < dup_extents->source_off + dup_extents->byte_count) {
      67           0 :                 DEBUG(2, ("dup_extents req exceeds src size\n"));
      68           0 :                 return NT_STATUS_NOT_SUPPORTED;
      69             :         }
      70             : 
      71           0 :         status = vfs_stat_fsp(dst_fsp);
      72           0 :         if (!NT_STATUS_IS_OK(status)) {
      73           0 :                 return status;
      74             :         }
      75             : 
      76           0 :         if (dst_fsp->fsp_name->st.st_ex_size
      77           0 :                         < dup_extents->target_off + dup_extents->byte_count) {
      78             : 
      79           0 :                 if (dst_fsp->fsp_name->st.st_ex_size - dup_extents->target_off
      80           0 :                                         > dst_fsp->fsp_name->st.st_ex_size) {
      81           0 :                         return NT_STATUS_INVALID_PARAMETER;     /* wrap */
      82             :                 }
      83             : 
      84             :                 /*
      85             :                  * this server behaviour is pretty hairy, but we need to match
      86             :                  * Windows, so...
      87             :                  */
      88           0 :                 DEBUG(2, ("dup_extents req exceeds target size, capping\n"));
      89           0 :                 dup_extents->byte_count = dst_fsp->fsp_name->st.st_ex_size
      90           0 :                                                 - dup_extents->target_off;
      91             :         }
      92             : 
      93           0 :         return NT_STATUS_OK;
      94             : }
      95             : 
      96           0 : static NTSTATUS fsctl_dup_extents_check_overlap(struct files_struct *src_fsp,
      97             :                                                 struct files_struct *dst_fsp,
      98             :                                 struct fsctl_dup_extents_to_file *dup_extents)
      99             : {
     100           0 :         if (!file_id_equal(&src_fsp->file_id, &dst_fsp->file_id)) {
     101             :                 /* src and dest refer to different files */
     102           0 :                 return NT_STATUS_OK;
     103             :         }
     104             : 
     105           0 :         if (sys_io_ranges_overlap(dup_extents->byte_count,
     106           0 :                                   dup_extents->source_off,
     107             :                                   dup_extents->byte_count,
     108           0 :                                   dup_extents->target_off))
     109             :         {
     110           0 :                 return NT_STATUS_NOT_SUPPORTED;
     111             :         }
     112             : 
     113           0 :         return NT_STATUS_OK;
     114             : }
     115             : 
     116           0 : static NTSTATUS fsctl_dup_extents_check_sparse(struct files_struct *src_fsp,
     117             :                                                struct files_struct *dst_fsp)
     118             : {
     119             :         /*
     120             :          * 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply...
     121             :          * STATUS_NOT_SUPPORTED: Target file is sparse, while source
     122             :          *                       is a non-sparse file.
     123             :          *
     124             :          * WS2016 has the following behaviour (MS are in the process of fixing
     125             :          * the spec):
     126             :          * STATUS_NOT_SUPPORTED is returned if the source is sparse, while the
     127             :          * target is non-sparse. However, if target is sparse while the source
     128             :          * is non-sparse, then FSCTL_DUPLICATE_EXTENTS_TO_FILE completes
     129             :          * successfully.
     130             :          */
     131           0 :         if (src_fsp->fsp_flags.is_sparse && !dst_fsp->fsp_flags.is_sparse) {
     132           0 :                 return NT_STATUS_NOT_SUPPORTED;
     133             :         }
     134             : 
     135           0 :         return NT_STATUS_OK;
     136             : }
     137             : 
     138             : struct fsctl_dup_extents_state {
     139             :         struct tevent_context *ev;
     140             :         struct connection_struct *conn;
     141             :         struct files_struct *dst_fsp;
     142             :         struct fsctl_dup_extents_to_file dup_extents;
     143             : };
     144             : 
     145             : static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq);
     146             : static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq);
     147             : 
     148           0 : static struct tevent_req *fsctl_dup_extents_send(TALLOC_CTX *mem_ctx,
     149             :                                                  struct tevent_context *ev,
     150             :                                                  struct files_struct *dst_fsp,
     151             :                                                  DATA_BLOB *in_input,
     152             :                                                  struct smbd_smb2_request *smb2req)
     153             : {
     154           0 :         struct tevent_req *req = NULL;
     155           0 :         struct tevent_req *subreq = NULL;
     156           0 :         struct fsctl_dup_extents_state *state = NULL;
     157           0 :         uint64_t src_fid_persistent = 0;
     158           0 :         uint64_t src_fid_volatile = 0;
     159           0 :         struct files_struct *src_fsp = NULL;
     160             :         int ndr_ret;
     161             :         NTSTATUS status;
     162             : 
     163           0 :         req = tevent_req_create(mem_ctx, &state,
     164             :                                 struct fsctl_dup_extents_state);
     165           0 :         if (req == NULL) {
     166           0 :                 return NULL;
     167             :         }
     168             : 
     169           0 :         if (dst_fsp == NULL) {
     170           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
     171           0 :                 return tevent_req_post(req, ev);
     172             :         }
     173             : 
     174           0 :         *state = (struct fsctl_dup_extents_state) {
     175           0 :                 .conn = dst_fsp->conn,
     176             :                 .ev = ev,
     177             :                 .dst_fsp = dst_fsp,
     178             :         };
     179             : 
     180           0 :         if ((dst_fsp->conn->fs_capabilities
     181           0 :                                 & FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0) {
     182           0 :                 DBG_INFO("FS does not advertise block refcounting support\n");
     183           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
     184           0 :                 return tevent_req_post(req, ev);
     185             :         }
     186             : 
     187           0 :         ndr_ret = ndr_pull_struct_blob(in_input, state, &state->dup_extents,
     188             :                        (ndr_pull_flags_fn_t)ndr_pull_fsctl_dup_extents_to_file);
     189           0 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     190           0 :                 DBG_ERR("failed to unmarshall dup extents to file req\n");
     191           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
     192           0 :                 return tevent_req_post(req, ev);
     193             :         }
     194             : 
     195           0 :         src_fid_persistent = BVAL(state->dup_extents.source_fid, 0);
     196           0 :         src_fid_volatile = BVAL(state->dup_extents.source_fid, 8);
     197           0 :         src_fsp = file_fsp_get(smb2req, src_fid_persistent, src_fid_volatile);
     198           0 :         if ((src_fsp == NULL)
     199           0 :                       || (src_fsp->file_id.devid != dst_fsp->file_id.devid)) {
     200             :                 /*
     201             :                  * [MS-FSCC] 2.3.8 FSCTL_DUPLICATE_EXTENTS_TO_FILE Reply
     202             :                  * STATUS_INVALID_PARAMETER:
     203             :                  * The FileHandle parameter is either invalid or does not
     204             :                  * represent a handle to an opened file on the same volume.
     205             :                  *
     206             :                  * Windows Server responds with NT_STATUS_INVALID_HANDLE instead
     207             :                  * of STATUS_INVALID_PARAMETER here, despite the above spec.
     208             :                  */
     209           0 :                 DBG_ERR("invalid src_fsp for dup_extents\n");
     210           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
     211           0 :                 return tevent_req_post(req, ev);
     212             :         }
     213             : 
     214           0 :         status = fsctl_dup_extents_check_lengths(src_fsp, dst_fsp,
     215           0 :                                                  &state->dup_extents);
     216           0 :         if (!NT_STATUS_IS_OK(status)) {
     217           0 :                 tevent_req_nterror(req, status);
     218           0 :                 return tevent_req_post(req, ev);
     219             :         }
     220             : 
     221           0 :         if (state->dup_extents.byte_count == 0) {
     222           0 :                 DBG_ERR("skipping zero length dup extents\n");
     223           0 :                 tevent_req_done(req);
     224           0 :                 return tevent_req_post(req, ev);
     225             :         }
     226             : 
     227           0 :         status = fsctl_dup_extents_check_overlap(src_fsp, dst_fsp,
     228           0 :                                                  &state->dup_extents);
     229           0 :         if (!NT_STATUS_IS_OK(status)) {
     230           0 :                 tevent_req_nterror(req, status);
     231           0 :                 return tevent_req_post(req, ev);
     232             :         }
     233             : 
     234           0 :         status = fsctl_dup_extents_check_sparse(src_fsp, dst_fsp);
     235           0 :         if (!NT_STATUS_IS_OK(status)) {
     236           0 :                 tevent_req_nterror(req, status);
     237           0 :                 return tevent_req_post(req, ev);
     238             :         }
     239             : 
     240           0 :         subreq = SMB_VFS_OFFLOAD_READ_SEND(state, ev, src_fsp,
     241             :                                            FSCTL_DUP_EXTENTS_TO_FILE,
     242             :                                            0, 0, 0);
     243           0 :         if (tevent_req_nomem(subreq, req)) {
     244           0 :                 return tevent_req_post(req, ev);
     245             :         }
     246           0 :         tevent_req_set_callback(subreq, fsctl_dup_extents_offload_read_done,
     247             :                                 req);
     248           0 :         return req;
     249             : }
     250             : 
     251           0 : static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
     252             : {
     253           0 :         struct tevent_req *req = tevent_req_callback_data(
     254             :                 subreq, struct tevent_req);
     255           0 :         struct fsctl_dup_extents_state *state = tevent_req_data(
     256             :                 req, struct fsctl_dup_extents_state);
     257             :         DATA_BLOB token;
     258             :         NTSTATUS status;
     259             : 
     260           0 :         status = SMB_VFS_OFFLOAD_READ_RECV(subreq, state->dst_fsp->conn,
     261             :                                            state, &token);
     262           0 :         if (tevent_req_nterror(req, status)) {
     263           0 :                 return;
     264             :         }
     265             : 
     266             :         /* tell the VFS to ignore locks across the clone, matching ReFS */
     267           0 :         subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
     268             :                                             state,
     269             :                                             state->ev,
     270             :                                             FSCTL_DUP_EXTENTS_TO_FILE,
     271             :                                             &token,
     272             :                                             state->dup_extents.source_off,
     273             :                                             state->dst_fsp,
     274             :                                             state->dup_extents.target_off,
     275             :                                             state->dup_extents.byte_count);
     276           0 :         if (tevent_req_nomem(subreq, req)) {
     277           0 :                 return;
     278             :         }
     279           0 :         tevent_req_set_callback(subreq, fsctl_dup_extents_vfs_done, req);
     280           0 :         return;
     281             : }
     282             : 
     283           0 : static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
     284             : {
     285           0 :         struct tevent_req *req = tevent_req_callback_data(
     286             :                 subreq, struct tevent_req);
     287           0 :         struct fsctl_dup_extents_state *state = tevent_req_data(
     288             :                 req, struct fsctl_dup_extents_state);
     289             :         off_t nb_chunk;
     290             :         NTSTATUS status;
     291             : 
     292           0 :         status = SMB_VFS_OFFLOAD_WRITE_RECV(state->conn, subreq, &nb_chunk);
     293           0 :         TALLOC_FREE(subreq);
     294           0 :         if (tevent_req_nterror(req, status)) {
     295           0 :                 return;
     296             :         }
     297             : 
     298           0 :         if (nb_chunk != state->dup_extents.byte_count) {
     299           0 :                 tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
     300           0 :                 return;
     301             :         }
     302             : 
     303           0 :         tevent_req_done(req);
     304             : }
     305             : 
     306           0 : static NTSTATUS fsctl_dup_extents_recv(struct tevent_req *req)
     307             : {
     308           0 :         return tevent_req_simple_recv_ntstatus(req);
     309             : }
     310             : 
     311           6 : static NTSTATUS fsctl_get_cmprn(TALLOC_CTX *mem_ctx,
     312             :                                 struct tevent_context *ev,
     313             :                                 struct files_struct *fsp,
     314             :                                 size_t in_max_output,
     315             :                                 DATA_BLOB *out_output)
     316             : {
     317             :         struct compression_state cmpr_state;
     318             :         enum ndr_err_code ndr_ret;
     319             :         DATA_BLOB output;
     320             :         NTSTATUS status;
     321             : 
     322           6 :         if (fsp == NULL) {
     323           0 :                 return NT_STATUS_FILE_CLOSED;
     324             :         }
     325             : 
     326             :         /* Windows doesn't check for SEC_FILE_READ_ATTRIBUTE permission here */
     327             : 
     328           6 :         ZERO_STRUCT(cmpr_state);
     329           6 :         if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
     330           0 :                 status = SMB_VFS_FGET_COMPRESSION(fsp->conn,
     331             :                                                  mem_ctx,
     332             :                                                  fsp,
     333             :                                                  &cmpr_state.format);
     334           0 :                 if (!NT_STATUS_IS_OK(status)) {
     335           0 :                         return status;
     336             :                 }
     337             :         } else {
     338             :                 /*
     339             :                  * bso#12144: The underlying filesystem doesn't support
     340             :                  * compression, so we should respond with "not-compressed"
     341             :                  * (like WS2016 ReFS) instead of STATUS_NOT_SUPPORTED or
     342             :                  * NT_STATUS_INVALID_DEVICE_REQUEST.
     343             :                  */
     344           6 :                 cmpr_state.format = COMPRESSION_FORMAT_NONE;
     345             :         }
     346             : 
     347           6 :         ndr_ret = ndr_push_struct_blob(&output, mem_ctx,
     348             :                                        &cmpr_state,
     349             :                         (ndr_push_flags_fn_t)ndr_push_compression_state);
     350           6 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     351           0 :                 return NT_STATUS_INTERNAL_ERROR;
     352             :         }
     353             : 
     354           6 :         if (in_max_output < output.length) {
     355           0 :                 DEBUG(1, ("max output %u too small for compression state %ld\n",
     356             :                       (unsigned int)in_max_output, (long int)output.length));
     357           0 :                 return NT_STATUS_INVALID_USER_BUFFER;
     358             :         }
     359           6 :         *out_output = output;
     360             : 
     361           6 :         return NT_STATUS_OK;
     362             : }
     363             : 
     364          12 : static NTSTATUS fsctl_set_cmprn(TALLOC_CTX *mem_ctx,
     365             :                                 struct tevent_context *ev,
     366             :                                 struct files_struct *fsp,
     367             :                                 DATA_BLOB *in_input)
     368             : {
     369             :         struct compression_state cmpr_state;
     370             :         enum ndr_err_code ndr_ret;
     371             :         NTSTATUS status;
     372             : 
     373          12 :         if (fsp == NULL) {
     374           0 :                 return NT_STATUS_FILE_CLOSED;
     375             :         }
     376             : 
     377             :         /* WRITE_DATA permission is required, WRITE_ATTRIBUTES is not */
     378          12 :         status = check_access_fsp(fsp, FILE_WRITE_DATA);
     379          12 :         if (!NT_STATUS_IS_OK(status)) {
     380           0 :                 return status;
     381             :         }
     382             : 
     383          12 :         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &cmpr_state,
     384             :                         (ndr_pull_flags_fn_t)ndr_pull_compression_state);
     385          12 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     386           0 :                 DEBUG(0, ("failed to unmarshall set compression req\n"));
     387           0 :                 return NT_STATUS_INVALID_PARAMETER;
     388             :         }
     389             : 
     390          12 :         status = NT_STATUS_NOT_SUPPORTED;
     391          12 :         if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
     392           0 :                 status = SMB_VFS_SET_COMPRESSION(fsp->conn,
     393             :                                                  mem_ctx,
     394             :                                                  fsp,
     395             :                                                  cmpr_state.format);
     396          12 :         } else if (cmpr_state.format == COMPRESSION_FORMAT_NONE) {
     397             :                 /*
     398             :                  * bso#12144: The underlying filesystem doesn't support
     399             :                  * compression. We should still accept set(FORMAT_NONE) requests
     400             :                  * (like WS2016 ReFS).
     401             :                  */
     402           6 :                 status = NT_STATUS_OK;
     403             :         }
     404             : 
     405          12 :         return status;
     406             : }
     407             : 
     408         194 : static NTSTATUS fsctl_zero_data(TALLOC_CTX *mem_ctx,
     409             :                                 struct tevent_context *ev,
     410             :                                 struct files_struct *fsp,
     411             :                                 DATA_BLOB *in_input)
     412             : {
     413             :         struct file_zero_data_info zdata_info;
     414             :         enum ndr_err_code ndr_ret;
     415             :         struct lock_struct lck;
     416             :         int mode;
     417             :         uint64_t len;
     418             :         int ret;
     419             :         NTSTATUS status;
     420             : 
     421         194 :         if (fsp == NULL) {
     422           0 :                 return NT_STATUS_FILE_CLOSED;
     423             :         }
     424             : 
     425             :         /* WRITE_DATA permission is required */
     426         194 :         status = check_access_fsp(fsp, FILE_WRITE_DATA);
     427         194 :         if (!NT_STATUS_IS_OK(status)) {
     428          18 :                 return status;
     429             :         }
     430             : 
     431             :         /* allow regardless of whether FS supports sparse or not */
     432             : 
     433         176 :         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &zdata_info,
     434             :                         (ndr_pull_flags_fn_t)ndr_pull_file_zero_data_info);
     435         176 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     436           0 :                 DEBUG(0, ("failed to unmarshall zero data request\n"));
     437           0 :                 return NT_STATUS_INVALID_PARAMETER;
     438             :         }
     439             : 
     440         176 :         if (zdata_info.beyond_final_zero < zdata_info.file_off) {
     441          12 :                 DEBUG(0, ("invalid zero data params: off %lu, bfz, %lu\n",
     442             :                           (unsigned long)zdata_info.file_off,
     443             :                           (unsigned long)zdata_info.beyond_final_zero));
     444          12 :                 return NT_STATUS_INVALID_PARAMETER;
     445             :         }
     446             : 
     447             :         /* convert strange "beyond final zero" param into length */
     448         164 :         len = zdata_info.beyond_final_zero - zdata_info.file_off;
     449             : 
     450         164 :         if (len == 0) {
     451          18 :                 DEBUG(2, ("zero data called with zero length range\n"));
     452          18 :                 return NT_STATUS_OK;
     453             :         }
     454             : 
     455         268 :         init_strict_lock_struct(fsp,
     456         146 :                                 fsp->op->global->open_persistent_id,
     457             :                                 zdata_info.file_off,
     458             :                                 len,
     459             :                                 WRITE_LOCK,
     460             :                                 &lck);
     461             : 
     462         146 :         if (!SMB_VFS_STRICT_LOCK_CHECK(fsp->conn, fsp, &lck)) {
     463           6 :                 DEBUG(2, ("failed to lock range for zero-data\n"));
     464           6 :                 return NT_STATUS_FILE_LOCK_CONFLICT;
     465             :         }
     466             : 
     467             :         /*
     468             :          * MS-FSCC <58> Section 2.3.67
     469             :          * This FSCTL sets the range of bytes to zero (0) without extending the
     470             :          * file size.
     471             :          *
     472             :          * The VFS_FALLOCATE_FL_KEEP_SIZE flag is used to satisfy this
     473             :          * constraint.
     474             :          */
     475             : 
     476         140 :         mode = VFS_FALLOCATE_FL_PUNCH_HOLE | VFS_FALLOCATE_FL_KEEP_SIZE;
     477         140 :         ret = SMB_VFS_FALLOCATE(fsp, mode, zdata_info.file_off, len);
     478         140 :         if (ret == -1)  {
     479           0 :                 status = map_nt_error_from_unix_common(errno);
     480           0 :                 DEBUG(2, ("zero-data fallocate(0x%x) failed: %s\n", mode,
     481             :                       strerror(errno)));
     482           0 :                 return status;
     483             :         }
     484             : 
     485         140 :         if (!fsp->fsp_flags.is_sparse && lp_strict_allocate(SNUM(fsp->conn))) {
     486             :                 /*
     487             :                  * File marked non-sparse and "strict allocate" is enabled -
     488             :                  * allocate the range that we just punched out.
     489             :                  * In future FALLOC_FL_ZERO_RANGE could be used exclusively for
     490             :                  * this, but it's currently only supported on XFS and ext4.
     491             :                  *
     492             :                  * The newly allocated range still won't be found by SEEK_DATA
     493             :                  * for QAR, but stat.st_blocks will reflect it.
     494             :                  */
     495           0 :                 ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_FL_KEEP_SIZE,
     496             :                                         zdata_info.file_off, len);
     497           0 :                 if (ret == -1)  {
     498           0 :                         status = map_nt_error_from_unix_common(errno);
     499           0 :                         DEBUG(0, ("fallocate failed: %s\n", strerror(errno)));
     500           0 :                         return status;
     501             :                 }
     502             :         }
     503             : 
     504         140 :         return NT_STATUS_OK;
     505             : }
     506             : 
     507         198 : static NTSTATUS fsctl_qar_buf_push(TALLOC_CTX *mem_ctx,
     508             :                                    struct file_alloced_range_buf *qar_buf,
     509             :                                    DATA_BLOB *qar_array_blob)
     510             : {
     511             :         DATA_BLOB new_slot;
     512             :         enum ndr_err_code ndr_ret;
     513             :         bool ok;
     514             : 
     515         198 :         ndr_ret = ndr_push_struct_blob(&new_slot, mem_ctx, qar_buf,
     516             :                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
     517         198 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     518           0 :                 DEBUG(0, ("failed to marshall QAR buf\n"));
     519           0 :                 return NT_STATUS_INVALID_PARAMETER;
     520             :         }
     521             : 
     522             :         /* TODO should be able to avoid copy by pushing into prealloced buf */
     523         198 :         ok = data_blob_append(mem_ctx, qar_array_blob, new_slot.data,
     524             :                               new_slot.length);
     525         198 :         data_blob_free(&new_slot);
     526         198 :         if (!ok) {
     527           0 :                 return NT_STATUS_NO_MEMORY;
     528             :         }
     529             : 
     530         198 :         return NT_STATUS_OK;
     531             : }
     532             : 
     533         114 : static NTSTATUS fsctl_qar_seek_fill(TALLOC_CTX *mem_ctx,
     534             :                                     struct files_struct *fsp,
     535             :                                     off_t curr_off,
     536             :                                     off_t max_off,
     537             :                                     DATA_BLOB *qar_array_blob)
     538             : {
     539         114 :         NTSTATUS status = NT_STATUS_NOT_SUPPORTED;
     540             : 
     541             : #ifdef HAVE_LSEEK_HOLE_DATA
     542         353 :         while (curr_off <= max_off) {
     543             :                 off_t data_off;
     544             :                 off_t hole_off;
     545             :                 struct file_alloced_range_buf qar_buf;
     546             : 
     547             :                 /* seek next data */
     548         186 :                 data_off = SMB_VFS_LSEEK(fsp, curr_off, SEEK_DATA);
     549         186 :                 if ((data_off == -1) && (errno == ENXIO)) {
     550             :                         /* no data from curr_off to EOF */
     551             :                         break;
     552         150 :                 } else if (data_off == -1) {
     553           0 :                         status = map_nt_error_from_unix_common(errno);
     554           0 :                         DEBUG(1, ("lseek data failed: %s\n", strerror(errno)));
     555           0 :                         return status;
     556             :                 }
     557             : 
     558         150 :                 if (data_off > max_off) {
     559             :                         /* found something, but passed range of interest */
     560           6 :                         break;
     561             :                 }
     562             : 
     563         144 :                 hole_off = SMB_VFS_LSEEK(fsp, data_off, SEEK_HOLE);
     564         144 :                 if (hole_off == -1) {
     565           0 :                         status = map_nt_error_from_unix_common(errno);
     566           0 :                         DEBUG(1, ("lseek hole failed: %s\n", strerror(errno)));
     567           0 :                         return status;
     568             :                 }
     569             : 
     570         144 :                 if (hole_off <= data_off) {
     571           0 :                         DEBUG(1, ("lseek inconsistent: hole %lu at or before "
     572             :                                   "data %lu\n", (unsigned long)hole_off,
     573             :                                   (unsigned long)data_off));
     574           0 :                         return NT_STATUS_INTERNAL_ERROR;
     575             :                 }
     576             : 
     577         144 :                 qar_buf.file_off = data_off;
     578             :                 /* + 1 to convert maximum offset to length */
     579         144 :                 qar_buf.len = MIN(hole_off, max_off + 1) - data_off;
     580             : 
     581         144 :                 status = fsctl_qar_buf_push(mem_ctx, &qar_buf, qar_array_blob);
     582         144 :                 if (!NT_STATUS_IS_OK(status)) {
     583           0 :                         return NT_STATUS_NO_MEMORY;
     584             :                 }
     585             : 
     586         144 :                 curr_off = hole_off;
     587             :         }
     588         114 :         status = NT_STATUS_OK;
     589             : #endif
     590             : 
     591         114 :         return status;
     592             : }
     593             : 
     594         252 : static NTSTATUS fsctl_qar(TALLOC_CTX *mem_ctx,
     595             :                           struct tevent_context *ev,
     596             :                           struct files_struct *fsp,
     597             :                           DATA_BLOB *in_input,
     598             :                           size_t in_max_output,
     599             :                           DATA_BLOB *out_output)
     600             : {
     601             :         struct fsctl_query_alloced_ranges_req qar_req;
     602             :         struct fsctl_query_alloced_ranges_rsp qar_rsp;
     603         252 :         DATA_BLOB qar_array_blob = data_blob_null;
     604             :         uint64_t max_off;
     605             :         enum ndr_err_code ndr_ret;
     606             :         int ret;
     607             :         NTSTATUS status;
     608             :         SMB_STRUCT_STAT sbuf;
     609             : 
     610         252 :         if (fsp == NULL) {
     611           0 :                 return NT_STATUS_FILE_CLOSED;
     612             :         }
     613             : 
     614             :         /* READ_DATA permission is required */
     615         252 :         status = check_access_fsp(fsp, FILE_READ_DATA);
     616         252 :         if (!NT_STATUS_IS_OK(status)) {
     617          12 :                 return status;
     618             :         }
     619             : 
     620         240 :         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &qar_req,
     621             :                 (ndr_pull_flags_fn_t)ndr_pull_fsctl_query_alloced_ranges_req);
     622         240 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     623           6 :                 DEBUG(0, ("failed to unmarshall QAR req\n"));
     624           6 :                 return NT_STATUS_INVALID_PARAMETER;
     625             :         }
     626             : 
     627             :         /*
     628             :          * XXX Windows Server 2008 & 2012 servers don't return lock-conflict
     629             :          * for QAR requests over an exclusively locked range!
     630             :          */
     631             : 
     632         234 :         ret = SMB_VFS_FSTAT(fsp, &sbuf);
     633         234 :         if (ret == -1) {
     634           0 :                 status = map_nt_error_from_unix_common(errno);
     635           0 :                 DEBUG(2, ("fstat failed: %s\n", strerror(errno)));
     636           0 :                 return status;
     637             :         }
     638             : 
     639         234 :         if ((qar_req.buf.len == 0)
     640         216 :          || (sbuf.st_ex_size == 0)
     641         192 :          || (qar_req.buf.file_off >= sbuf.st_ex_size)) {
     642             :                 /* zero length range or after EOF, no ranges to return */
     643          60 :                 return NT_STATUS_OK;
     644             :         }
     645             : 
     646             :         /* check for integer overflow */
     647         174 :         if (qar_req.buf.file_off + qar_req.buf.len < qar_req.buf.file_off) {
     648           6 :                 return NT_STATUS_INVALID_PARAMETER;
     649             :         }
     650             : 
     651             :         /*
     652             :          * Maximum offset is either the last valid offset _before_ EOF, or the
     653             :          * last byte offset within the requested range. -1 converts length to
     654             :          * offset, which is easier to work with for SEEK_DATA/SEEK_HOLE, E.g.:
     655             :          *
     656             :          * /off=0             /off=512K          /st_ex_size=1M
     657             :          * |-------------------------------------|
     658             :          * | File data                           |
     659             :          * |-------------------------------------|
     660             :          *                                                   QAR end\
     661             :          *                    |=====================================|
     662             :          *                    |    QAR off=512K, len=1M             |
     663             :          *                    |=================^===================|
     664             :          *                                   max_off=1M - 1
     665             :          *             QAR end\
     666             :          * |==================|
     667             :          * |QAR off=0 len=512K|
     668             :          * |==================|
     669             :          *                   ^
     670             :          *                max_off=512K - 1
     671             :          */
     672         168 :         max_off = MIN(sbuf.st_ex_size,
     673             :                       qar_req.buf.file_off + qar_req.buf.len) - 1;
     674             : 
     675         168 :         if (!fsp->fsp_flags.is_sparse) {
     676             :                 struct file_alloced_range_buf qar_buf;
     677             : 
     678             :                 /* file is non-sparse, claim file_off->max_off is allocated */
     679          54 :                 qar_buf.file_off = qar_req.buf.file_off;
     680             :                 /* + 1 to convert maximum offset back to length */
     681          54 :                 qar_buf.len = max_off - qar_req.buf.file_off + 1;
     682             : 
     683          54 :                 status = fsctl_qar_buf_push(mem_ctx, &qar_buf, &qar_array_blob);
     684             :         } else {
     685         114 :                 status = fsctl_qar_seek_fill(mem_ctx, fsp, qar_req.buf.file_off,
     686             :                                              max_off, &qar_array_blob);
     687             :         }
     688         168 :         if (!NT_STATUS_IS_OK(status)) {
     689           0 :                 return status;
     690             :         }
     691             : 
     692             :         /* marshall response buffer. */
     693         168 :         qar_rsp.far_buf_array = qar_array_blob;
     694             : 
     695         168 :         ndr_ret = ndr_push_struct_blob(out_output, mem_ctx, &qar_rsp,
     696             :                 (ndr_push_flags_fn_t)ndr_push_fsctl_query_alloced_ranges_rsp);
     697         168 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     698           0 :                 DEBUG(0, ("failed to marshall QAR rsp\n"));
     699           0 :                 return NT_STATUS_INVALID_PARAMETER;
     700             :         }
     701             : 
     702         168 :         if (out_output->length > in_max_output) {
     703           6 :                 DEBUG(2, ("QAR output len %lu exceeds max %lu\n",
     704             :                           (unsigned long)out_output->length,
     705             :                           (unsigned long)in_max_output));
     706           6 :                 data_blob_free(out_output);
     707           6 :                 return NT_STATUS_BUFFER_TOO_SMALL;
     708             :         }
     709             : 
     710         162 :         return NT_STATUS_OK;
     711             : }
     712             : 
     713             : static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq);
     714             : 
     715         708 : struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code,
     716             :                                       struct tevent_context *ev,
     717             :                                       struct tevent_req *req,
     718             :                                       struct smbd_smb2_ioctl_state *state)
     719             : {
     720             :         NTSTATUS status;
     721             : 
     722         708 :         switch (ctl_code) {
     723           6 :         case FSCTL_GET_COMPRESSION:
     724          11 :                 status = fsctl_get_cmprn(state, ev, state->fsp,
     725           6 :                                          state->in_max_output,
     726             :                                          &state->out_output);
     727           6 :                 if (!tevent_req_nterror(req, status)) {
     728           6 :                         tevent_req_done(req);
     729             :                 }
     730           6 :                 return tevent_req_post(req, ev);
     731             :                 break;
     732          12 :         case FSCTL_SET_COMPRESSION:
     733          12 :                 status = fsctl_set_cmprn(state, ev, state->fsp,
     734             :                                          &state->in_input);
     735          12 :                 if (!tevent_req_nterror(req, status)) {
     736           6 :                         tevent_req_done(req);
     737             :                 }
     738          12 :                 return tevent_req_post(req, ev);
     739             :                 break;
     740         194 :         case FSCTL_SET_ZERO_DATA:
     741         194 :                 status = fsctl_zero_data(state, ev, state->fsp,
     742             :                                          &state->in_input);
     743         194 :                 if (!tevent_req_nterror(req, status)) {
     744         158 :                         tevent_req_done(req);
     745             :                 }
     746         194 :                 return tevent_req_post(req, ev);
     747             :                 break;
     748         252 :         case FSCTL_QUERY_ALLOCATED_RANGES:
     749         462 :                 status = fsctl_qar(state, ev, state->fsp,
     750             :                                    &state->in_input,
     751         252 :                                    state->in_max_output,
     752             :                                    &state->out_output);
     753         252 :                 if (!tevent_req_nterror(req, status)) {
     754         222 :                         tevent_req_done(req);
     755             :                 }
     756         252 :                 return tevent_req_post(req, ev);
     757             :                 break;
     758           0 :         case FSCTL_DUP_EXTENTS_TO_FILE: {
     759           0 :                 struct tevent_req *subreq = NULL;
     760             : 
     761           0 :                 subreq = fsctl_dup_extents_send(state, ev,
     762           0 :                                                 state->fsp,
     763             :                                                 &state->in_input,
     764             :                                                 state->smb2req);
     765           0 :                 if (tevent_req_nomem(subreq, req)) {
     766           0 :                         return tevent_req_post(req, ev);
     767             :                 }
     768           0 :                 tevent_req_set_callback(subreq,
     769             :                                         smb2_ioctl_filesys_dup_extents_done,
     770             :                                         req);
     771           0 :                 return req;
     772             :                 break;
     773             :         }
     774         244 :         default: {
     775         244 :                 uint8_t *out_data = NULL;
     776         244 :                 uint32_t out_data_len = 0;
     777             : 
     778         244 :                 if (state->fsp == NULL) {
     779           0 :                         status = NT_STATUS_NOT_SUPPORTED;
     780             :                 } else {
     781         244 :                         status = SMB_VFS_FSCTL(state->fsp,
     782             :                                                state,
     783             :                                                ctl_code,
     784             :                                                state->smbreq->flags2,
     785             :                                                state->in_input.data,
     786             :                                                state->in_input.length,
     787             :                                                &out_data,
     788             :                                                state->in_max_output,
     789             :                                                &out_data_len);
     790         244 :                         state->out_output = data_blob_const(out_data, out_data_len);
     791         244 :                         if (NT_STATUS_IS_OK(status)) {
     792         218 :                                 tevent_req_done(req);
     793         218 :                                 return tevent_req_post(req, ev);
     794             :                         }
     795             :                 }
     796             : 
     797          26 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
     798           0 :                         if (IS_IPC(state->smbreq->conn)) {
     799           0 :                                 status = NT_STATUS_FS_DRIVER_REQUIRED;
     800             :                         } else {
     801           0 :                                 status = NT_STATUS_INVALID_DEVICE_REQUEST;
     802             :                         }
     803             :                 }
     804             : 
     805          26 :                 tevent_req_nterror(req, status);
     806          26 :                 return tevent_req_post(req, ev);
     807             :                 break;
     808             :         }
     809             :         }
     810             : 
     811             :         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
     812             :         return tevent_req_post(req, ev);
     813             : }
     814             : 
     815           0 : static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq)
     816             : {
     817           0 :         struct tevent_req *req = tevent_req_callback_data(subreq,
     818             :                                                           struct tevent_req);
     819             :         NTSTATUS status;
     820             : 
     821           0 :         status = fsctl_dup_extents_recv(subreq);
     822           0 :         TALLOC_FREE(subreq);
     823           0 :         if (!tevent_req_nterror(req, status)) {
     824           0 :                 tevent_req_done(req);
     825             :         }
     826           0 : }

Generated by: LCOV version 1.13