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

Generated by: LCOV version 1.13