LCOV - code coverage report
Current view: top level - source3/smbd - smb2_ioctl_filesys.c (source / functions) Hit Total Coverage
Test: coverage report for fix-15632 9995c5c2 Lines: 139 359 38.7 %
Date: 2024-04-13 12:30:31 Functions: 7 15 46.7 %

          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           0 :         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           0 :         int ndr_ret;
     161           0 :         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 (tevent_req_nterror(req, status)) {
     217           0 :                 return tevent_req_post(req, ev);
     218             :         }
     219             : 
     220           0 :         if (state->dup_extents.byte_count == 0) {
     221           0 :                 DBG_ERR("skipping zero length dup extents\n");
     222           0 :                 tevent_req_done(req);
     223           0 :                 return tevent_req_post(req, ev);
     224             :         }
     225             : 
     226           0 :         status = fsctl_dup_extents_check_overlap(src_fsp, dst_fsp,
     227           0 :                                                  &state->dup_extents);
     228           0 :         if (tevent_req_nterror(req, status)) {
     229           0 :                 return tevent_req_post(req, ev);
     230             :         }
     231             : 
     232           0 :         status = fsctl_dup_extents_check_sparse(src_fsp, dst_fsp);
     233           0 :         if (tevent_req_nterror(req, status)) {
     234           0 :                 return tevent_req_post(req, ev);
     235             :         }
     236             : 
     237           0 :         subreq = SMB_VFS_OFFLOAD_READ_SEND(state, ev, src_fsp,
     238             :                                            FSCTL_DUP_EXTENTS_TO_FILE,
     239             :                                            0, 0, 0);
     240           0 :         if (tevent_req_nomem(subreq, req)) {
     241           0 :                 return tevent_req_post(req, ev);
     242             :         }
     243           0 :         tevent_req_set_callback(subreq, fsctl_dup_extents_offload_read_done,
     244             :                                 req);
     245           0 :         return req;
     246             : }
     247             : 
     248           0 : static void fsctl_dup_extents_offload_read_done(struct tevent_req *subreq)
     249             : {
     250           0 :         struct tevent_req *req = tevent_req_callback_data(
     251             :                 subreq, struct tevent_req);
     252           0 :         struct fsctl_dup_extents_state *state = tevent_req_data(
     253             :                 req, struct fsctl_dup_extents_state);
     254           0 :         uint32_t flags;
     255           0 :         uint64_t xferlen;
     256           0 :         DATA_BLOB token;
     257           0 :         NTSTATUS status;
     258             : 
     259             :         /*
     260             :          * Note that both flags and xferlen are not used with copy-chunk.
     261             :          */
     262             : 
     263           0 :         status = SMB_VFS_OFFLOAD_READ_RECV(subreq, state->dst_fsp->conn,
     264             :                                            state, &flags, &xferlen, &token);
     265           0 :         if (tevent_req_nterror(req, status)) {
     266           0 :                 return;
     267             :         }
     268             : 
     269             :         /* tell the VFS to ignore locks across the clone, matching ReFS */
     270           0 :         subreq = SMB_VFS_OFFLOAD_WRITE_SEND(state->dst_fsp->conn,
     271             :                                             state,
     272             :                                             state->ev,
     273             :                                             FSCTL_DUP_EXTENTS_TO_FILE,
     274             :                                             &token,
     275             :                                             state->dup_extents.source_off,
     276             :                                             state->dst_fsp,
     277             :                                             state->dup_extents.target_off,
     278             :                                             state->dup_extents.byte_count);
     279           0 :         if (tevent_req_nomem(subreq, req)) {
     280           0 :                 return;
     281             :         }
     282           0 :         tevent_req_set_callback(subreq, fsctl_dup_extents_vfs_done, req);
     283           0 :         return;
     284             : }
     285             : 
     286           0 : static void fsctl_dup_extents_vfs_done(struct tevent_req *subreq)
     287             : {
     288           0 :         struct tevent_req *req = tevent_req_callback_data(
     289             :                 subreq, struct tevent_req);
     290           0 :         struct fsctl_dup_extents_state *state = tevent_req_data(
     291             :                 req, struct fsctl_dup_extents_state);
     292           0 :         off_t nb_chunk;
     293           0 :         NTSTATUS status;
     294             : 
     295           0 :         status = SMB_VFS_OFFLOAD_WRITE_RECV(state->conn, subreq, &nb_chunk);
     296           0 :         TALLOC_FREE(subreq);
     297           0 :         if (tevent_req_nterror(req, status)) {
     298           0 :                 return;
     299             :         }
     300             : 
     301           0 :         if (nb_chunk != state->dup_extents.byte_count) {
     302           0 :                 tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
     303           0 :                 return;
     304             :         }
     305             : 
     306           0 :         tevent_req_done(req);
     307             : }
     308             : 
     309           0 : static NTSTATUS fsctl_dup_extents_recv(struct tevent_req *req)
     310             : {
     311           0 :         return tevent_req_simple_recv_ntstatus(req);
     312             : }
     313             : 
     314           8 : static NTSTATUS fsctl_get_cmprn(TALLOC_CTX *mem_ctx,
     315             :                                 struct tevent_context *ev,
     316             :                                 struct files_struct *fsp,
     317             :                                 size_t in_max_output,
     318             :                                 DATA_BLOB *out_output)
     319             : {
     320           0 :         struct compression_state cmpr_state;
     321           0 :         enum ndr_err_code ndr_ret;
     322           0 :         DATA_BLOB output;
     323           0 :         NTSTATUS status;
     324             : 
     325           8 :         if (fsp == NULL) {
     326           0 :                 return NT_STATUS_FILE_CLOSED;
     327             :         }
     328             : 
     329             :         /* Windows doesn't check for SEC_FILE_READ_ATTRIBUTE permission here */
     330             : 
     331           8 :         ZERO_STRUCT(cmpr_state);
     332           8 :         if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
     333           0 :                 status = SMB_VFS_FGET_COMPRESSION(fsp->conn,
     334             :                                                  mem_ctx,
     335             :                                                  fsp,
     336             :                                                  &cmpr_state.format);
     337           0 :                 if (!NT_STATUS_IS_OK(status)) {
     338           0 :                         return status;
     339             :                 }
     340             :         } else {
     341             :                 /*
     342             :                  * bso#12144: The underlying filesystem doesn't support
     343             :                  * compression, so we should respond with "not-compressed"
     344             :                  * (like WS2016 ReFS) instead of STATUS_NOT_SUPPORTED or
     345             :                  * NT_STATUS_INVALID_DEVICE_REQUEST.
     346             :                  */
     347           8 :                 cmpr_state.format = COMPRESSION_FORMAT_NONE;
     348             :         }
     349             : 
     350           8 :         ndr_ret = ndr_push_struct_blob(&output, mem_ctx,
     351             :                                        &cmpr_state,
     352             :                         (ndr_push_flags_fn_t)ndr_push_compression_state);
     353           8 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     354           0 :                 return NT_STATUS_INTERNAL_ERROR;
     355             :         }
     356             : 
     357           8 :         if (in_max_output < output.length) {
     358           0 :                 DEBUG(1, ("max output %u too small for compression state %ld\n",
     359             :                       (unsigned int)in_max_output, (long int)output.length));
     360           0 :                 return NT_STATUS_INVALID_USER_BUFFER;
     361             :         }
     362           8 :         *out_output = output;
     363             : 
     364           8 :         return NT_STATUS_OK;
     365             : }
     366             : 
     367          16 : static NTSTATUS fsctl_set_cmprn(TALLOC_CTX *mem_ctx,
     368             :                                 struct tevent_context *ev,
     369             :                                 struct files_struct *fsp,
     370             :                                 DATA_BLOB *in_input)
     371             : {
     372           0 :         struct compression_state cmpr_state;
     373           0 :         enum ndr_err_code ndr_ret;
     374           0 :         NTSTATUS status;
     375             : 
     376          16 :         if (fsp == NULL) {
     377           0 :                 return NT_STATUS_FILE_CLOSED;
     378             :         }
     379             : 
     380             :         /* WRITE_DATA permission is required, WRITE_ATTRIBUTES is not */
     381          16 :         status = check_any_access_fsp(fsp, FILE_WRITE_DATA);
     382          16 :         if (!NT_STATUS_IS_OK(status)) {
     383           0 :                 return status;
     384             :         }
     385             : 
     386          16 :         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &cmpr_state,
     387             :                         (ndr_pull_flags_fn_t)ndr_pull_compression_state);
     388          16 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     389           0 :                 DEBUG(0, ("failed to unmarshall set compression req\n"));
     390           0 :                 return NT_STATUS_INVALID_PARAMETER;
     391             :         }
     392             : 
     393          16 :         status = NT_STATUS_NOT_SUPPORTED;
     394          16 :         if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
     395           0 :                 status = SMB_VFS_SET_COMPRESSION(fsp->conn,
     396             :                                                  mem_ctx,
     397             :                                                  fsp,
     398             :                                                  cmpr_state.format);
     399          16 :         } else if (cmpr_state.format == COMPRESSION_FORMAT_NONE) {
     400             :                 /*
     401             :                  * bso#12144: The underlying filesystem doesn't support
     402             :                  * compression. We should still accept set(FORMAT_NONE) requests
     403             :                  * (like WS2016 ReFS).
     404             :                  */
     405           8 :                 status = NT_STATUS_OK;
     406             :         }
     407             : 
     408          16 :         return status;
     409             : }
     410             : 
     411         258 : static NTSTATUS fsctl_zero_data(TALLOC_CTX *mem_ctx,
     412             :                                 struct tevent_context *ev,
     413             :                                 struct files_struct *fsp,
     414             :                                 DATA_BLOB *in_input)
     415             : {
     416           0 :         struct file_zero_data_info zdata_info;
     417           0 :         enum ndr_err_code ndr_ret;
     418           0 :         struct lock_struct lck;
     419           0 :         int mode;
     420           0 :         uint64_t len;
     421           0 :         int ret;
     422           0 :         NTSTATUS status;
     423             : 
     424         258 :         if (fsp == NULL) {
     425           0 :                 return NT_STATUS_FILE_CLOSED;
     426             :         }
     427             : 
     428             :         /* WRITE_DATA permission is required */
     429         258 :         status = check_any_access_fsp(fsp, FILE_WRITE_DATA);
     430         258 :         if (!NT_STATUS_IS_OK(status)) {
     431          24 :                 return status;
     432             :         }
     433             : 
     434             :         /* allow regardless of whether FS supports sparse or not */
     435             : 
     436         234 :         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &zdata_info,
     437             :                         (ndr_pull_flags_fn_t)ndr_pull_file_zero_data_info);
     438         234 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     439           0 :                 DEBUG(0, ("failed to unmarshall zero data request\n"));
     440           0 :                 return NT_STATUS_INVALID_PARAMETER;
     441             :         }
     442             : 
     443         234 :         if (zdata_info.beyond_final_zero < zdata_info.file_off) {
     444          16 :                 DEBUG(0, ("invalid zero data params: off %lu, bfz, %lu\n",
     445             :                           (unsigned long)zdata_info.file_off,
     446             :                           (unsigned long)zdata_info.beyond_final_zero));
     447          16 :                 return NT_STATUS_INVALID_PARAMETER;
     448             :         }
     449             : 
     450             :         /* convert strange "beyond final zero" param into length */
     451         218 :         len = zdata_info.beyond_final_zero - zdata_info.file_off;
     452             : 
     453         218 :         if (len == 0) {
     454          24 :                 DEBUG(2, ("zero data called with zero length range\n"));
     455          24 :                 return NT_STATUS_OK;
     456             :         }
     457             : 
     458         194 :         init_strict_lock_struct(fsp,
     459         194 :                                 fsp->op->global->open_persistent_id,
     460             :                                 zdata_info.file_off,
     461             :                                 len,
     462             :                                 WRITE_LOCK,
     463             :                                 lp_posix_cifsu_locktype(fsp),
     464             :                                 &lck);
     465             : 
     466         194 :         if (!SMB_VFS_STRICT_LOCK_CHECK(fsp->conn, fsp, &lck)) {
     467           8 :                 DEBUG(2, ("failed to lock range for zero-data\n"));
     468           8 :                 return NT_STATUS_FILE_LOCK_CONFLICT;
     469             :         }
     470             : 
     471             :         /*
     472             :          * MS-FSCC <58> Section 2.3.67
     473             :          * This FSCTL sets the range of bytes to zero (0) without extending the
     474             :          * file size.
     475             :          *
     476             :          * The VFS_FALLOCATE_FL_KEEP_SIZE flag is used to satisfy this
     477             :          * constraint.
     478             :          */
     479             : 
     480         186 :         mode = VFS_FALLOCATE_FL_PUNCH_HOLE | VFS_FALLOCATE_FL_KEEP_SIZE;
     481         186 :         ret = SMB_VFS_FALLOCATE(fsp, mode, zdata_info.file_off, len);
     482         186 :         if (ret == -1)  {
     483           0 :                 status = map_nt_error_from_unix_common(errno);
     484           0 :                 DEBUG(2, ("zero-data fallocate(0x%x) failed: %s\n", mode,
     485             :                       strerror(errno)));
     486           0 :                 return status;
     487             :         }
     488             : 
     489         186 :         if (!fsp->fsp_flags.is_sparse && lp_strict_allocate(SNUM(fsp->conn))) {
     490             :                 /*
     491             :                  * File marked non-sparse and "strict allocate" is enabled -
     492             :                  * allocate the range that we just punched out.
     493             :                  * In future FALLOC_FL_ZERO_RANGE could be used exclusively for
     494             :                  * this, but it's currently only supported on XFS and ext4.
     495             :                  *
     496             :                  * The newly allocated range still won't be found by SEEK_DATA
     497             :                  * for QAR, but stat.st_blocks will reflect it.
     498             :                  */
     499           0 :                 ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_FL_KEEP_SIZE,
     500             :                                         zdata_info.file_off, len);
     501           0 :                 if (ret == -1)  {
     502           0 :                         status = map_nt_error_from_unix_common(errno);
     503           0 :                         DEBUG(0, ("fallocate failed: %s\n", strerror(errno)));
     504           0 :                         return status;
     505             :                 }
     506             :         }
     507             : 
     508         186 :         return NT_STATUS_OK;
     509             : }
     510             : 
     511         264 : static NTSTATUS fsctl_qar_buf_push(TALLOC_CTX *mem_ctx,
     512             :                                    struct file_alloced_range_buf *qar_buf,
     513             :                                    DATA_BLOB *qar_array_blob)
     514             : {
     515           0 :         DATA_BLOB new_slot;
     516           0 :         enum ndr_err_code ndr_ret;
     517           0 :         bool ok;
     518             : 
     519         264 :         ndr_ret = ndr_push_struct_blob(&new_slot, mem_ctx, qar_buf,
     520             :                         (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
     521         264 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     522           0 :                 DEBUG(0, ("failed to marshall QAR buf\n"));
     523           0 :                 return NT_STATUS_INVALID_PARAMETER;
     524             :         }
     525             : 
     526             :         /* TODO should be able to avoid copy by pushing into prealloced buf */
     527         264 :         ok = data_blob_append(mem_ctx, qar_array_blob, new_slot.data,
     528             :                               new_slot.length);
     529         264 :         data_blob_free(&new_slot);
     530         264 :         if (!ok) {
     531           0 :                 return NT_STATUS_NO_MEMORY;
     532             :         }
     533             : 
     534         264 :         return NT_STATUS_OK;
     535             : }
     536             : 
     537         152 : static NTSTATUS fsctl_qar_seek_fill(TALLOC_CTX *mem_ctx,
     538             :                                     struct files_struct *fsp,
     539             :                                     off_t curr_off,
     540             :                                     off_t max_off,
     541             :                                     DATA_BLOB *qar_array_blob)
     542             : {
     543         152 :         NTSTATUS status = NT_STATUS_NOT_SUPPORTED;
     544             : 
     545             : #ifdef HAVE_LSEEK_HOLE_DATA
     546         344 :         while (curr_off <= max_off) {
     547           0 :                 off_t data_off;
     548           0 :                 off_t hole_off;
     549           0 :                 struct file_alloced_range_buf qar_buf;
     550             : 
     551             :                 /* seek next data */
     552         248 :                 data_off = SMB_VFS_LSEEK(fsp, curr_off, SEEK_DATA);
     553         248 :                 if ((data_off == -1) && (errno == ENXIO)) {
     554             :                         /* no data from curr_off to EOF */
     555             :                         break;
     556         200 :                 } else if (data_off == -1) {
     557           0 :                         status = map_nt_error_from_unix_common(errno);
     558           0 :                         DEBUG(1, ("lseek data failed: %s\n", strerror(errno)));
     559           0 :                         return status;
     560             :                 }
     561             : 
     562         200 :                 if (data_off > max_off) {
     563             :                         /* found something, but passed range of interest */
     564           8 :                         break;
     565             :                 }
     566             : 
     567         192 :                 hole_off = SMB_VFS_LSEEK(fsp, data_off, SEEK_HOLE);
     568         192 :                 if (hole_off == -1) {
     569           0 :                         status = map_nt_error_from_unix_common(errno);
     570           0 :                         DEBUG(1, ("lseek hole failed: %s\n", strerror(errno)));
     571           0 :                         return status;
     572             :                 }
     573             : 
     574         192 :                 if (hole_off <= data_off) {
     575           0 :                         DEBUG(1, ("lseek inconsistent: hole %lu at or before "
     576             :                                   "data %lu\n", (unsigned long)hole_off,
     577             :                                   (unsigned long)data_off));
     578           0 :                         return NT_STATUS_INTERNAL_ERROR;
     579             :                 }
     580             : 
     581         192 :                 qar_buf.file_off = data_off;
     582             :                 /* + 1 to convert maximum offset to length */
     583         192 :                 qar_buf.len = MIN(hole_off, max_off + 1) - data_off;
     584             : 
     585         192 :                 status = fsctl_qar_buf_push(mem_ctx, &qar_buf, qar_array_blob);
     586         192 :                 if (!NT_STATUS_IS_OK(status)) {
     587           0 :                         return NT_STATUS_NO_MEMORY;
     588             :                 }
     589             : 
     590         192 :                 curr_off = hole_off;
     591             :         }
     592         152 :         status = NT_STATUS_OK;
     593             : #endif
     594             : 
     595         152 :         return status;
     596             : }
     597             : 
     598         336 : static NTSTATUS fsctl_qar(TALLOC_CTX *mem_ctx,
     599             :                           struct tevent_context *ev,
     600             :                           struct files_struct *fsp,
     601             :                           DATA_BLOB *in_input,
     602             :                           size_t in_max_output,
     603             :                           DATA_BLOB *out_output)
     604             : {
     605           0 :         struct fsctl_query_alloced_ranges_req qar_req;
     606           0 :         struct fsctl_query_alloced_ranges_rsp qar_rsp;
     607         336 :         DATA_BLOB qar_array_blob = data_blob_null;
     608           0 :         uint64_t max_off;
     609           0 :         enum ndr_err_code ndr_ret;
     610           0 :         int ret;
     611           0 :         NTSTATUS status;
     612           0 :         SMB_STRUCT_STAT sbuf;
     613             : 
     614         336 :         if (fsp == NULL) {
     615           0 :                 return NT_STATUS_FILE_CLOSED;
     616             :         }
     617             : 
     618             :         /* READ_DATA permission is required */
     619         336 :         status = check_any_access_fsp(fsp, FILE_READ_DATA);
     620         336 :         if (!NT_STATUS_IS_OK(status)) {
     621          16 :                 return status;
     622             :         }
     623             : 
     624         320 :         ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &qar_req,
     625             :                 (ndr_pull_flags_fn_t)ndr_pull_fsctl_query_alloced_ranges_req);
     626         320 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     627           8 :                 DEBUG(0, ("failed to unmarshall QAR req\n"));
     628           8 :                 return NT_STATUS_INVALID_PARAMETER;
     629             :         }
     630             : 
     631             :         /*
     632             :          * XXX Windows Server 2008 & 2012 servers don't return lock-conflict
     633             :          * for QAR requests over an exclusively locked range!
     634             :          */
     635             : 
     636         312 :         ret = SMB_VFS_FSTAT(fsp, &sbuf);
     637         312 :         if (ret == -1) {
     638           0 :                 status = map_nt_error_from_unix_common(errno);
     639           0 :                 DEBUG(2, ("fstat failed: %s\n", strerror(errno)));
     640           0 :                 return status;
     641             :         }
     642             : 
     643         312 :         if ((qar_req.buf.len == 0)
     644         288 :          || (sbuf.st_ex_size == 0)
     645         256 :          || (qar_req.buf.file_off >= sbuf.st_ex_size)) {
     646             :                 /* zero length range or after EOF, no ranges to return */
     647          80 :                 return NT_STATUS_OK;
     648             :         }
     649             : 
     650             :         /* check for integer overflow */
     651         232 :         if (qar_req.buf.file_off + qar_req.buf.len < qar_req.buf.file_off) {
     652           8 :                 return NT_STATUS_INVALID_PARAMETER;
     653             :         }
     654             : 
     655             :         /*
     656             :          * Maximum offset is either the last valid offset _before_ EOF, or the
     657             :          * last byte offset within the requested range. -1 converts length to
     658             :          * offset, which is easier to work with for SEEK_DATA/SEEK_HOLE, E.g.:
     659             :          *
     660             :          * /off=0             /off=512K          /st_ex_size=1M
     661             :          * |-------------------------------------|
     662             :          * | File data                           |
     663             :          * |-------------------------------------|
     664             :          *                                                   QAR end\
     665             :          *                    |=====================================|
     666             :          *                    |    QAR off=512K, len=1M             |
     667             :          *                    |=================^===================|
     668             :          *                                   max_off=1M - 1
     669             :          *             QAR end\
     670             :          * |==================|
     671             :          * |QAR off=0 len=512K|
     672             :          * |==================|
     673             :          *                   ^
     674             :          *                max_off=512K - 1
     675             :          */
     676         224 :         max_off = MIN(sbuf.st_ex_size,
     677             :                       qar_req.buf.file_off + qar_req.buf.len) - 1;
     678             : 
     679         224 :         if (!fsp->fsp_flags.is_sparse) {
     680           0 :                 struct file_alloced_range_buf qar_buf;
     681             : 
     682             :                 /* file is non-sparse, claim file_off->max_off is allocated */
     683          72 :                 qar_buf.file_off = qar_req.buf.file_off;
     684             :                 /* + 1 to convert maximum offset back to length */
     685          72 :                 qar_buf.len = max_off - qar_req.buf.file_off + 1;
     686             : 
     687          72 :                 status = fsctl_qar_buf_push(mem_ctx, &qar_buf, &qar_array_blob);
     688             :         } else {
     689         152 :                 status = fsctl_qar_seek_fill(mem_ctx, fsp, qar_req.buf.file_off,
     690             :                                              max_off, &qar_array_blob);
     691             :         }
     692         224 :         if (!NT_STATUS_IS_OK(status)) {
     693           0 :                 return status;
     694             :         }
     695             : 
     696             :         /* marshall response buffer. */
     697         224 :         qar_rsp.far_buf_array = qar_array_blob;
     698             : 
     699         224 :         ndr_ret = ndr_push_struct_blob(out_output, mem_ctx, &qar_rsp,
     700             :                 (ndr_push_flags_fn_t)ndr_push_fsctl_query_alloced_ranges_rsp);
     701         224 :         if (ndr_ret != NDR_ERR_SUCCESS) {
     702           0 :                 DEBUG(0, ("failed to marshall QAR rsp\n"));
     703           0 :                 return NT_STATUS_INVALID_PARAMETER;
     704             :         }
     705             : 
     706         224 :         if (out_output->length > in_max_output) {
     707           8 :                 DEBUG(2, ("QAR output len %lu exceeds max %lu\n",
     708             :                           (unsigned long)out_output->length,
     709             :                           (unsigned long)in_max_output));
     710           8 :                 data_blob_free(out_output);
     711           8 :                 return NT_STATUS_BUFFER_TOO_SMALL;
     712             :         }
     713             : 
     714         216 :         return NT_STATUS_OK;
     715             : }
     716             : 
     717             : static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq);
     718             : 
     719         918 : struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code,
     720             :                                       struct tevent_context *ev,
     721             :                                       struct tevent_req *req,
     722             :                                       struct smbd_smb2_ioctl_state *state)
     723             : {
     724           0 :         NTSTATUS status;
     725             : 
     726         918 :         switch (ctl_code) {
     727           8 :         case FSCTL_GET_COMPRESSION:
     728           8 :                 status = fsctl_get_cmprn(state, ev, state->fsp,
     729           8 :                                          state->in_max_output,
     730             :                                          &state->out_output);
     731           8 :                 if (!tevent_req_nterror(req, status)) {
     732           8 :                         tevent_req_done(req);
     733             :                 }
     734           8 :                 return tevent_req_post(req, ev);
     735           0 :                 break;
     736          16 :         case FSCTL_SET_COMPRESSION:
     737          16 :                 status = fsctl_set_cmprn(state, ev, state->fsp,
     738             :                                          &state->in_input);
     739          16 :                 if (!tevent_req_nterror(req, status)) {
     740           8 :                         tevent_req_done(req);
     741             :                 }
     742          16 :                 return tevent_req_post(req, ev);
     743           0 :                 break;
     744         258 :         case FSCTL_SET_ZERO_DATA:
     745         258 :                 status = fsctl_zero_data(state, ev, state->fsp,
     746             :                                          &state->in_input);
     747         258 :                 if (!tevent_req_nterror(req, status)) {
     748         210 :                         tevent_req_done(req);
     749             :                 }
     750         258 :                 return tevent_req_post(req, ev);
     751           0 :                 break;
     752         336 :         case FSCTL_QUERY_ALLOCATED_RANGES:
     753         336 :                 status = fsctl_qar(state, ev, state->fsp,
     754             :                                    &state->in_input,
     755         336 :                                    state->in_max_output,
     756             :                                    &state->out_output);
     757         336 :                 if (!tevent_req_nterror(req, status)) {
     758         296 :                         tevent_req_done(req);
     759             :                 }
     760         336 :                 return tevent_req_post(req, ev);
     761           0 :                 break;
     762           0 :         case FSCTL_DUP_EXTENTS_TO_FILE: {
     763           0 :                 struct tevent_req *subreq = NULL;
     764             : 
     765           0 :                 subreq = fsctl_dup_extents_send(state, ev,
     766           0 :                                                 state->fsp,
     767             :                                                 &state->in_input,
     768             :                                                 state->smb2req);
     769           0 :                 if (tevent_req_nomem(subreq, req)) {
     770           0 :                         return tevent_req_post(req, ev);
     771             :                 }
     772           0 :                 tevent_req_set_callback(subreq,
     773             :                                         smb2_ioctl_filesys_dup_extents_done,
     774             :                                         req);
     775           0 :                 return req;
     776           0 :                 break;
     777             :         }
     778         300 :         default: {
     779         300 :                 uint8_t *out_data = NULL;
     780         300 :                 uint32_t out_data_len = 0;
     781             : 
     782         300 :                 if (state->fsp == NULL) {
     783           0 :                         status = NT_STATUS_NOT_SUPPORTED;
     784             :                 } else {
     785         300 :                         status = SMB_VFS_FSCTL(state->fsp,
     786             :                                                state,
     787             :                                                ctl_code,
     788             :                                                state->smbreq->flags2,
     789             :                                                state->in_input.data,
     790             :                                                state->in_input.length,
     791             :                                                &out_data,
     792             :                                                state->in_max_output,
     793             :                                                &out_data_len);
     794         300 :                         state->out_output = data_blob_const(out_data, out_data_len);
     795         300 :                         if (NT_STATUS_IS_OK(status)) {
     796         270 :                                 tevent_req_done(req);
     797         270 :                                 return tevent_req_post(req, ev);
     798             :                         }
     799             :                 }
     800             : 
     801          30 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
     802           0 :                         if (IS_IPC(state->smbreq->conn)) {
     803           0 :                                 status = NT_STATUS_FS_DRIVER_REQUIRED;
     804             :                         } else {
     805           0 :                                 status = NT_STATUS_INVALID_DEVICE_REQUEST;
     806             :                         }
     807             :                 }
     808             : 
     809          30 :                 tevent_req_nterror(req, status);
     810          30 :                 return tevent_req_post(req, ev);
     811           0 :                 break;
     812             :         }
     813             :         }
     814             : 
     815             :         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
     816             :         return tevent_req_post(req, ev);
     817             : }
     818             : 
     819           0 : static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq)
     820             : {
     821           0 :         struct tevent_req *req = tevent_req_callback_data(subreq,
     822             :                                                           struct tevent_req);
     823           0 :         NTSTATUS status;
     824             : 
     825           0 :         status = fsctl_dup_extents_recv(subreq);
     826           0 :         TALLOC_FREE(subreq);
     827           0 :         if (!tevent_req_nterror(req, status)) {
     828           0 :                 tevent_req_done(req);
     829             :         }
     830           0 : }

Generated by: LCOV version 1.14