LCOV - code coverage report
Current view: top level - source3/smbd - smb2_write.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 138 179 77.1 %
Date: 2021-09-23 10:06:22 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Core SMB2 server
       4             : 
       5             :    Copyright (C) Stefan Metzmacher 2009
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "smbd/smbd.h"
      23             : #include "smbd/globals.h"
      24             : #include "../libcli/smb/smb_common.h"
      25             : #include "../lib/util/tevent_ntstatus.h"
      26             : #include "rpc_server/srv_pipe_hnd.h"
      27             : 
      28             : #undef DBGC_CLASS
      29             : #define DBGC_CLASS DBGC_SMB2
      30             : 
      31             : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
      32             :                                                struct tevent_context *ev,
      33             :                                                struct smbd_smb2_request *smb2req,
      34             :                                                struct files_struct *in_fsp,
      35             :                                                DATA_BLOB in_data,
      36             :                                                uint64_t in_offset,
      37             :                                                uint32_t in_flags);
      38             : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
      39             :                                      uint32_t *out_count);
      40             : 
      41             : static void smbd_smb2_request_write_done(struct tevent_req *subreq);
      42       46368 : NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
      43             : {
      44       46368 :         struct smbXsrv_connection *xconn = req->xconn;
      45             :         NTSTATUS status;
      46             :         const uint8_t *inbody;
      47             :         uint16_t in_data_offset;
      48             :         uint32_t in_data_length;
      49             :         DATA_BLOB in_data_buffer;
      50             :         uint64_t in_offset;
      51             :         uint64_t in_file_id_persistent;
      52             :         uint64_t in_file_id_volatile;
      53             :         struct files_struct *in_fsp;
      54             :         uint32_t in_flags;
      55       46368 :         size_t in_dyn_len = 0;
      56       46368 :         uint8_t *in_dyn_ptr = NULL;
      57             :         struct tevent_req *subreq;
      58             : 
      59       46368 :         status = smbd_smb2_request_verify_sizes(req, 0x31);
      60       46368 :         if (!NT_STATUS_IS_OK(status)) {
      61           0 :                 return smbd_smb2_request_error(req, status);
      62             :         }
      63       46368 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
      64             : 
      65       46368 :         in_data_offset          = SVAL(inbody, 0x02);
      66       46368 :         in_data_length          = IVAL(inbody, 0x04);
      67       46368 :         in_offset               = BVAL(inbody, 0x08);
      68       46368 :         in_file_id_persistent   = BVAL(inbody, 0x10);
      69       46368 :         in_file_id_volatile     = BVAL(inbody, 0x18);
      70       46368 :         in_flags                = IVAL(inbody, 0x2C);
      71             : 
      72       46368 :         if (in_data_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
      73           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      74             :         }
      75             : 
      76       46368 :         if (req->smb1req != NULL && req->smb1req->unread_bytes > 0) {
      77           0 :                 in_dyn_ptr = NULL;
      78           0 :                 in_dyn_len = req->smb1req->unread_bytes;
      79             :         } else {
      80       46368 :                 in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req);
      81       46368 :                 in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req);
      82             :         }
      83             : 
      84       46368 :         if (in_data_length > in_dyn_len) {
      85           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      86             :         }
      87             : 
      88             :         /* check the max write size */
      89       46368 :         if (in_data_length > xconn->smb2.server.max_write) {
      90           0 :                 DEBUG(2,("smbd_smb2_request_process_write : "
      91             :                         "client ignored max write :%s: 0x%08X: 0x%08X\n",
      92             :                         __location__, in_data_length, xconn->smb2.server.max_write));
      93           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      94             :         }
      95             : 
      96             :         /*
      97             :          * Note: that in_dyn_ptr is NULL for the recvfile case.
      98             :          */
      99       46368 :         in_data_buffer.data = in_dyn_ptr;
     100       46368 :         in_data_buffer.length = in_data_length;
     101             : 
     102       46368 :         status = smbd_smb2_request_verify_creditcharge(req, in_data_length);
     103       46368 :         if (!NT_STATUS_IS_OK(status)) {
     104           0 :                 return smbd_smb2_request_error(req, status);
     105             :         }
     106             : 
     107       46368 :         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
     108       46368 :         if (in_fsp == NULL) {
     109           0 :                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
     110             :         }
     111             : 
     112       46368 :         subreq = smbd_smb2_write_send(req, req->sconn->ev_ctx,
     113             :                                       req, in_fsp,
     114             :                                       in_data_buffer,
     115             :                                       in_offset,
     116             :                                       in_flags);
     117       46368 :         if (subreq == NULL) {
     118           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     119             :         }
     120       46368 :         tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
     121             : 
     122       46368 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
     123             : }
     124             : 
     125       46364 : static void smbd_smb2_request_write_done(struct tevent_req *subreq)
     126             : {
     127       46364 :         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
     128             :                                         struct smbd_smb2_request);
     129             :         DATA_BLOB outbody;
     130             :         DATA_BLOB outdyn;
     131       46364 :         uint32_t out_count = 0;
     132             :         NTSTATUS status;
     133             :         NTSTATUS error; /* transport error */
     134             : 
     135       46364 :         status = smbd_smb2_write_recv(subreq, &out_count);
     136       46364 :         TALLOC_FREE(subreq);
     137       46364 :         if (!NT_STATUS_IS_OK(status)) {
     138        2408 :                 error = smbd_smb2_request_error(req, status);
     139        2408 :                 if (!NT_STATUS_IS_OK(error)) {
     140           0 :                         smbd_server_connection_terminate(req->xconn,
     141             :                                                          nt_errstr(error));
     142          48 :                         return;
     143             :                 }
     144        2408 :                 return;
     145             :         }
     146             : 
     147       43956 :         outbody = smbd_smb2_generate_outbody(req, 0x10);
     148       43956 :         if (outbody.data == NULL) {
     149           0 :                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     150           0 :                 if (!NT_STATUS_IS_OK(error)) {
     151           0 :                         smbd_server_connection_terminate(req->xconn,
     152             :                                                          nt_errstr(error));
     153           0 :                         return;
     154             :                 }
     155           0 :                 return;
     156             :         }
     157             : 
     158       43956 :         SSVAL(outbody.data, 0x00, 0x10 + 1);    /* struct size */
     159       43956 :         SSVAL(outbody.data, 0x02, 0);           /* reserved */
     160       43956 :         SIVAL(outbody.data, 0x04, out_count);   /* count */
     161       43956 :         SIVAL(outbody.data, 0x08, 0);           /* remaining */
     162       43956 :         SSVAL(outbody.data, 0x0C, 0);           /* write channel info offset */
     163       43956 :         SSVAL(outbody.data, 0x0E, 0);           /* write channel info length */
     164             : 
     165       43956 :         outdyn = data_blob_const(NULL, 0);
     166             : 
     167       43956 :         error = smbd_smb2_request_done(req, outbody, &outdyn);
     168       43956 :         if (!NT_STATUS_IS_OK(error)) {
     169           0 :                 smbd_server_connection_terminate(req->xconn, nt_errstr(error));
     170           0 :                 return;
     171             :         }
     172             : }
     173             : 
     174             : struct smbd_smb2_write_state {
     175             :         struct smbd_smb2_request *smb2req;
     176             :         struct smb_request *smbreq;
     177             :         files_struct *fsp;
     178             :         bool write_through;
     179             :         uint32_t in_length;
     180             :         uint64_t in_offset;
     181             :         uint32_t out_count;
     182             : };
     183             : 
     184             : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq);
     185             : 
     186       38957 : static NTSTATUS smb2_write_complete_internal(struct tevent_req *req,
     187             :                                              ssize_t nwritten, int err,
     188             :                                              bool do_sync)
     189             : {
     190             :         NTSTATUS status;
     191       38957 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     192             :                                         struct smbd_smb2_write_state);
     193       38957 :         files_struct *fsp = state->fsp;
     194             : 
     195       38957 :         if (nwritten == -1) {
     196        2230 :                 if (err == EOVERFLOW &&
     197           0 :                     is_ntfs_stream_smb_fname(fsp->fsp_name)) {
     198           0 :                         status = NT_STATUS_FILE_SYSTEM_LIMITATION;
     199             :                 } else {
     200        2230 :                         status = map_nt_error_from_unix(err);
     201             :                 }
     202             : 
     203        2230 :                 DEBUG(2, ("smb2_write failed: %s, file %s, "
     204             :                           "length=%lu offset=%lu nwritten=-1: %s\n",
     205             :                           fsp_fnum_dbg(fsp),
     206             :                           fsp_str_dbg(fsp),
     207             :                           (unsigned long)state->in_length,
     208             :                           (unsigned long)state->in_offset,
     209             :                           nt_errstr(status)));
     210             : 
     211        2230 :                 return status;
     212             :         }
     213             : 
     214       36727 :         DEBUG(3,("smb2: %s, file %s, "
     215             :                 "length=%lu offset=%lu wrote=%lu\n",
     216             :                 fsp_fnum_dbg(fsp),
     217             :                 fsp_str_dbg(fsp),
     218             :                 (unsigned long)state->in_length,
     219             :                 (unsigned long)state->in_offset,
     220             :                 (unsigned long)nwritten));
     221             : 
     222       36727 :         if ((nwritten == 0) && (state->in_length != 0)) {
     223           0 :                 DEBUG(5,("smb2: write [%s] disk full\n",
     224             :                         fsp_str_dbg(fsp)));
     225           0 :                 return NT_STATUS_DISK_FULL;
     226             :         }
     227             : 
     228       36727 :         if (do_sync) {
     229         878 :                 status = sync_file(fsp->conn, fsp, state->write_through);
     230         878 :                 if (!NT_STATUS_IS_OK(status)) {
     231           0 :                         DEBUG(5,("smb2: sync_file for %s returned %s\n",
     232             :                                  fsp_str_dbg(fsp),
     233             :                                  nt_errstr(status)));
     234           0 :                         return status;
     235             :                 }
     236             :         }
     237             : 
     238       36727 :         state->out_count = nwritten;
     239             : 
     240       36727 :         return NT_STATUS_OK;
     241             : }
     242             : 
     243        3088 : NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
     244             : {
     245        3088 :         return smb2_write_complete_internal(req, nwritten, err, true);
     246             : }
     247             : 
     248       35869 : NTSTATUS smb2_write_complete_nosync(struct tevent_req *req, ssize_t nwritten,
     249             :                                     int err)
     250             : {
     251       35869 :         return smb2_write_complete_internal(req, nwritten, err, false);
     252             : }
     253             : 
     254             : 
     255           0 : static bool smbd_smb2_write_cancel(struct tevent_req *req)
     256             : {
     257           0 :         struct smbd_smb2_write_state *state =
     258           0 :                 tevent_req_data(req,
     259             :                 struct smbd_smb2_write_state);
     260             : 
     261           0 :         return cancel_smb2_aio(state->smbreq);
     262             : }
     263             : 
     264       46368 : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
     265             :                                                struct tevent_context *ev,
     266             :                                                struct smbd_smb2_request *smb2req,
     267             :                                                struct files_struct *fsp,
     268             :                                                DATA_BLOB in_data,
     269             :                                                uint64_t in_offset,
     270             :                                                uint32_t in_flags)
     271             : {
     272             :         NTSTATUS status;
     273       46368 :         struct tevent_req *req = NULL;
     274       46368 :         struct smbd_smb2_write_state *state = NULL;
     275       46368 :         struct smb_request *smbreq = NULL;
     276       46368 :         connection_struct *conn = smb2req->tcon->compat;
     277             :         ssize_t nwritten;
     278             :         struct lock_struct lock;
     279             : 
     280       46368 :         req = tevent_req_create(mem_ctx, &state,
     281             :                                 struct smbd_smb2_write_state);
     282       46368 :         if (req == NULL) {
     283           0 :                 return NULL;
     284             :         }
     285       46368 :         state->smb2req = smb2req;
     286       46368 :         if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
     287       41650 :                 if (in_flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) {
     288           2 :                         state->write_through = true;
     289             :                 }
     290             :         }
     291       46368 :         if (in_flags & SMB2_WRITEFLAG_WRITE_THROUGH) {
     292           2 :                 state->write_through = true;
     293             :         }
     294       46368 :         state->in_length = in_data.length;
     295       46368 :         state->in_offset = in_offset;
     296       46368 :         state->out_count = 0;
     297             : 
     298       46368 :         DEBUG(10,("smbd_smb2_write: %s - %s\n",
     299             :                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
     300             : 
     301       46368 :         smbreq = smbd_smb2_fake_smb_request(smb2req);
     302       46368 :         if (tevent_req_nomem(smbreq, req)) {
     303           0 :                 return tevent_req_post(req, ev);
     304             :         }
     305       46368 :         state->smbreq = smbreq;
     306             : 
     307       46368 :         state->fsp = fsp;
     308             : 
     309       46368 :         if (IS_IPC(smbreq->conn)) {
     310        7229 :                 struct tevent_req *subreq = NULL;
     311             : 
     312        7229 :                 if (!fsp_is_np(fsp)) {
     313           0 :                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
     314           0 :                         return tevent_req_post(req, ev);
     315             :                 }
     316             : 
     317       12226 :                 subreq = np_write_send(state, ev,
     318             :                                        fsp->fake_file_handle,
     319        7182 :                                        in_data.data,
     320             :                                        in_data.length);
     321        7229 :                 if (tevent_req_nomem(subreq, req)) {
     322           0 :                         return tevent_req_post(req, ev);
     323             :                 }
     324        7229 :                 tevent_req_set_callback(subreq,
     325             :                                         smbd_smb2_write_pipe_done,
     326             :                                         req);
     327        7229 :                 return req;
     328             :         }
     329             : 
     330       39139 :         if (!CHECK_WRITE(fsp)) {
     331         166 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     332         166 :                 return tevent_req_post(req, ev);
     333             :         }
     334             : 
     335             :         /* Try and do an asynchronous write. */
     336       38973 :         status = schedule_aio_smb2_write(conn,
     337             :                                         smbreq,
     338             :                                         fsp,
     339             :                                         in_offset,
     340             :                                         in_data,
     341       38973 :                                         state->write_through);
     342             : 
     343       38973 :         if (NT_STATUS_IS_OK(status)) {
     344             :                 /*
     345             :                  * Doing an async write, allow this
     346             :                  * request to be canceled
     347             :                  */
     348       35873 :                 tevent_req_set_cancel_fn(req, smbd_smb2_write_cancel);
     349       35873 :                 return req;
     350             :         }
     351             : 
     352        3100 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
     353             :                 /* Real error in setting up aio. Fail. */
     354          12 :                 tevent_req_nterror(req, status);
     355          12 :                 return tevent_req_post(req, ev);
     356             :         }
     357             : 
     358             :         /* Fallback to synchronous. */
     359        6152 :         init_strict_lock_struct(fsp,
     360        3088 :                                 fsp->op->global->open_persistent_id,
     361             :                                 in_offset,
     362             :                                 in_data.length,
     363             :                                 WRITE_LOCK,
     364             :                                 &lock);
     365             : 
     366        3088 :         if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &lock)) {
     367           0 :                 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
     368           0 :                 return tevent_req_post(req, ev);
     369             :         }
     370             : 
     371             :         /*
     372             :          * Note: in_data.data is NULL for the recvfile case.
     373             :          */
     374        6152 :         nwritten = write_file(smbreq, fsp,
     375        3088 :                               (const char *)in_data.data,
     376             :                               in_offset,
     377             :                               in_data.length);
     378             : 
     379        3088 :         status = smb2_write_complete(req, nwritten, errno);
     380             : 
     381        3088 :         DEBUG(10,("smb2: write on "
     382             :                 "file %s, offset %.0f, requested %u, written = %u\n",
     383             :                 fsp_str_dbg(fsp),
     384             :                 (double)in_offset,
     385             :                 (unsigned int)in_data.length,
     386             :                 (unsigned int)nwritten ));
     387             : 
     388        3088 :         if (!NT_STATUS_IS_OK(status)) {
     389        2210 :                 tevent_req_nterror(req, status);
     390             :         } else {
     391             :                 /* Success. */
     392         878 :                 tevent_req_done(req);
     393             :         }
     394             : 
     395        3088 :         return tevent_req_post(req, ev);
     396             : }
     397             : 
     398        7229 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
     399             : {
     400        7229 :         struct tevent_req *req = tevent_req_callback_data(subreq,
     401             :                                  struct tevent_req);
     402        7229 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     403             :                                               struct smbd_smb2_write_state);
     404             :         NTSTATUS status;
     405        7229 :         ssize_t nwritten = -1;
     406             : 
     407        7229 :         status = np_write_recv(subreq, &nwritten);
     408        7229 :         TALLOC_FREE(subreq);
     409        7229 :         if (!NT_STATUS_IS_OK(status)) {
     410           0 :                 NTSTATUS old = status;
     411           0 :                 status = nt_status_np_pipe(old);
     412           0 :                 tevent_req_nterror(req, status);
     413           0 :                 return;
     414             :         }
     415             : 
     416        7229 :         if ((nwritten == 0 && state->in_length != 0) || (nwritten < 0)) {
     417           0 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     418           0 :                 return;
     419             :         }
     420             : 
     421        7229 :         state->out_count = nwritten;
     422             : 
     423        7229 :         tevent_req_done(req);
     424             : }
     425             : 
     426       46364 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
     427             :                                      uint32_t *out_count)
     428             : {
     429             :         NTSTATUS status;
     430       46364 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     431             :                                               struct smbd_smb2_write_state);
     432             : 
     433       46364 :         if (tevent_req_is_nterror(req, &status)) {
     434        2408 :                 tevent_req_received(req);
     435        2408 :                 return status;
     436             :         }
     437             : 
     438       43956 :         *out_count = state->out_count;
     439             : 
     440       43956 :         tevent_req_received(req);
     441       43956 :         return NT_STATUS_OK;
     442             : }

Generated by: LCOV version 1.13