LCOV - code coverage report
Current view: top level - source3/smbd - smb2_write.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 160 204 78.4 %
Date: 2023-11-21 12:31:41 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       63724 : NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
      43             : {
      44       63724 :         struct smbXsrv_connection *xconn = req->xconn;
      45          47 :         NTSTATUS status;
      46          47 :         const uint8_t *inbody;
      47          47 :         uint16_t in_data_offset;
      48          47 :         uint32_t in_data_length;
      49          47 :         DATA_BLOB in_data_buffer;
      50          47 :         uint64_t in_offset;
      51          47 :         uint64_t in_file_id_persistent;
      52          47 :         uint64_t in_file_id_volatile;
      53          47 :         struct files_struct *in_fsp;
      54          47 :         uint32_t in_flags;
      55       63724 :         size_t in_dyn_len = 0;
      56       63724 :         uint8_t *in_dyn_ptr = NULL;
      57          47 :         struct tevent_req *subreq;
      58             : 
      59       63724 :         status = smbd_smb2_request_verify_sizes(req, 0x31);
      60       63724 :         if (!NT_STATUS_IS_OK(status)) {
      61           0 :                 return smbd_smb2_request_error(req, status);
      62             :         }
      63       63724 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
      64             : 
      65       63724 :         in_data_offset          = SVAL(inbody, 0x02);
      66       63724 :         in_data_length          = IVAL(inbody, 0x04);
      67       63724 :         in_offset               = BVAL(inbody, 0x08);
      68       63724 :         in_file_id_persistent   = BVAL(inbody, 0x10);
      69       63724 :         in_file_id_volatile     = BVAL(inbody, 0x18);
      70       63724 :         in_flags                = IVAL(inbody, 0x2C);
      71             : 
      72       63724 :         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       63724 :         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       63724 :                 in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req);
      81       63724 :                 in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req);
      82             :         }
      83             : 
      84       63724 :         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       63724 :         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       63724 :         in_data_buffer.data = in_dyn_ptr;
     100       63724 :         in_data_buffer.length = in_data_length;
     101             : 
     102       63724 :         status = smbd_smb2_request_verify_creditcharge(req, in_data_length);
     103       63724 :         if (!NT_STATUS_IS_OK(status)) {
     104           0 :                 return smbd_smb2_request_error(req, status);
     105             :         }
     106             : 
     107       63724 :         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
     108       63724 :         if (in_fsp == NULL) {
     109           0 :                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
     110             :         }
     111             : 
     112       63724 :         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       63724 :         if (subreq == NULL) {
     118           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     119             :         }
     120       63724 :         tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
     121             : 
     122       63724 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
     123             : }
     124             : 
     125       63720 : static void smbd_smb2_request_write_done(struct tevent_req *subreq)
     126             : {
     127       63720 :         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
     128             :                                         struct smbd_smb2_request);
     129          47 :         DATA_BLOB outbody;
     130          47 :         DATA_BLOB outdyn;
     131       63720 :         uint32_t out_count = 0;
     132          47 :         NTSTATUS status;
     133          47 :         NTSTATUS error; /* transport error */
     134             : 
     135       63720 :         status = smbd_smb2_write_recv(subreq, &out_count);
     136       63720 :         TALLOC_FREE(subreq);
     137       63720 :         if (!NT_STATUS_IS_OK(status)) {
     138        2424 :                 error = smbd_smb2_request_error(req, status);
     139        2424 :                 if (!NT_STATUS_IS_OK(error)) {
     140           0 :                         smbd_server_connection_terminate(req->xconn,
     141             :                                                          nt_errstr(error));
     142        2424 :                         return;
     143             :                 }
     144        2424 :                 return;
     145             :         }
     146             : 
     147       61296 :         outbody = smbd_smb2_generate_outbody(req, 0x10);
     148       61296 :         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       61296 :         SSVAL(outbody.data, 0x00, 0x10 + 1);    /* struct size */
     159       61296 :         SSVAL(outbody.data, 0x02, 0);           /* reserved */
     160       61296 :         SIVAL(outbody.data, 0x04, out_count);   /* count */
     161       61296 :         SIVAL(outbody.data, 0x08, 0);           /* remaining */
     162       61296 :         SSVAL(outbody.data, 0x0C, 0);           /* write channel info offset */
     163       61296 :         SSVAL(outbody.data, 0x0E, 0);           /* write channel info length */
     164             : 
     165       61296 :         outdyn = data_blob_const(NULL, 0);
     166             : 
     167       61296 :         error = smbd_smb2_request_done(req, outbody, &outdyn);
     168       61296 :         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       56405 : static NTSTATUS smb2_write_complete_internal(struct tevent_req *req,
     187             :                                              ssize_t nwritten, int err,
     188             :                                              bool do_sync)
     189             : {
     190           0 :         NTSTATUS status;
     191       56405 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     192             :                                         struct smbd_smb2_write_state);
     193       56405 :         files_struct *fsp = state->fsp;
     194             : 
     195       56405 :         if (nwritten == -1) {
     196        2246 :                 if (err == EOVERFLOW && fsp_is_alternate_stream(fsp)) {
     197           0 :                         status = NT_STATUS_FILE_SYSTEM_LIMITATION;
     198             :                 } else {
     199        2246 :                         status = map_nt_error_from_unix(err);
     200             :                 }
     201             : 
     202        2246 :                 DEBUG(2, ("smb2_write failed: %s, file %s, "
     203             :                           "length=%lu offset=%lu nwritten=-1: %s\n",
     204             :                           fsp_fnum_dbg(fsp),
     205             :                           fsp_str_dbg(fsp),
     206             :                           (unsigned long)state->in_length,
     207             :                           (unsigned long)state->in_offset,
     208             :                           nt_errstr(status)));
     209             : 
     210        2246 :                 return status;
     211             :         }
     212             : 
     213       54159 :         DEBUG(3,("smb2: %s, file %s, "
     214             :                 "length=%lu offset=%lu wrote=%lu\n",
     215             :                 fsp_fnum_dbg(fsp),
     216             :                 fsp_str_dbg(fsp),
     217             :                 (unsigned long)state->in_length,
     218             :                 (unsigned long)state->in_offset,
     219             :                 (unsigned long)nwritten));
     220             : 
     221       54159 :         if ((nwritten == 0) && (state->in_length != 0)) {
     222           0 :                 DEBUG(5,("smb2: write [%s] disk full\n",
     223             :                         fsp_str_dbg(fsp)));
     224           0 :                 return NT_STATUS_DISK_FULL;
     225             :         }
     226             : 
     227       54159 :         if (do_sync) {
     228         926 :                 status = sync_file(fsp->conn, fsp, state->write_through);
     229         926 :                 if (!NT_STATUS_IS_OK(status)) {
     230           0 :                         DEBUG(5,("smb2: sync_file for %s returned %s\n",
     231             :                                  fsp_str_dbg(fsp),
     232             :                                  nt_errstr(status)));
     233           0 :                         return status;
     234             :                 }
     235             :         }
     236             : 
     237       54159 :         state->out_count = nwritten;
     238             : 
     239       54159 :         return NT_STATUS_OK;
     240             : }
     241             : 
     242        3142 : NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
     243             : {
     244        3142 :         return smb2_write_complete_internal(req, nwritten, err, true);
     245             : }
     246             : 
     247       53263 : NTSTATUS smb2_write_complete_nosync(struct tevent_req *req, ssize_t nwritten,
     248             :                                     int err)
     249             : {
     250       53263 :         return smb2_write_complete_internal(req, nwritten, err, false);
     251             : }
     252             : 
     253             : 
     254           0 : static bool smbd_smb2_write_cancel(struct tevent_req *req)
     255             : {
     256           0 :         struct smbd_smb2_write_state *state =
     257           0 :                 tevent_req_data(req,
     258             :                 struct smbd_smb2_write_state);
     259             : 
     260           0 :         return cancel_smb2_aio(state->smbreq);
     261             : }
     262             : 
     263       63724 : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
     264             :                                                struct tevent_context *ev,
     265             :                                                struct smbd_smb2_request *smb2req,
     266             :                                                struct files_struct *fsp,
     267             :                                                DATA_BLOB in_data,
     268             :                                                uint64_t in_offset,
     269             :                                                uint32_t in_flags)
     270             : {
     271          47 :         NTSTATUS status;
     272       63724 :         struct tevent_req *req = NULL;
     273       63724 :         struct smbd_smb2_write_state *state = NULL;
     274       63724 :         struct smb_request *smbreq = NULL;
     275       63724 :         connection_struct *conn = smb2req->tcon->compat;
     276          47 :         ssize_t nwritten;
     277          47 :         struct lock_struct lock;
     278             : 
     279       63724 :         req = tevent_req_create(mem_ctx, &state,
     280             :                                 struct smbd_smb2_write_state);
     281       63724 :         if (req == NULL) {
     282           0 :                 return NULL;
     283             :         }
     284       63724 :         state->smb2req = smb2req;
     285       63724 :         if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
     286       58916 :                 if (in_flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) {
     287           0 :                         state->write_through = true;
     288             :                 }
     289             :         }
     290       63724 :         if (in_flags & SMB2_WRITEFLAG_WRITE_THROUGH) {
     291           4 :                 state->write_through = true;
     292             :         }
     293       63724 :         state->in_length = in_data.length;
     294       63724 :         state->in_offset = in_offset;
     295       63724 :         state->out_count = 0;
     296             : 
     297       63724 :         DEBUG(10,("smbd_smb2_write: %s - %s\n",
     298             :                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
     299             : 
     300       63724 :         smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
     301       63724 :         if (tevent_req_nomem(smbreq, req)) {
     302           0 :                 return tevent_req_post(req, ev);
     303             :         }
     304       63724 :         state->smbreq = smbreq;
     305             : 
     306       63724 :         state->fsp = fsp;
     307             : 
     308       63724 :         if (IS_IPC(smbreq->conn)) {
     309        7137 :                 struct tevent_req *subreq = NULL;
     310          47 :                 bool ok;
     311             : 
     312        7137 :                 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        7137 :                 subreq = np_write_send(state, ev,
     318             :                                        fsp->fake_file_handle,
     319        7090 :                                        in_data.data,
     320             :                                        in_data.length);
     321        7137 :                 if (tevent_req_nomem(subreq, req)) {
     322           0 :                         return tevent_req_post(req, ev);
     323             :                 }
     324        7137 :                 tevent_req_set_callback(subreq,
     325             :                                         smbd_smb2_write_pipe_done,
     326             :                                         req);
     327             : 
     328             :                 /*
     329             :                  * Make sure we mark the fsp as having outstanding async
     330             :                  * activity so we don't crash on shutdown close.
     331             :                  */
     332             : 
     333        7137 :                 ok = aio_add_req_to_fsp(fsp, req);
     334        7137 :                 if (!ok) {
     335           0 :                         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
     336           0 :                         return tevent_req_post(req, ev);
     337             :                 }
     338             : 
     339        7090 :                 return req;
     340             :         }
     341             : 
     342       56587 :         if (!CHECK_WRITE(fsp)) {
     343         166 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     344         166 :                 return tevent_req_post(req, ev);
     345             :         }
     346             : 
     347             :         /* Try and do an asynchronous write. */
     348       56421 :         status = schedule_aio_smb2_write(conn,
     349             :                                         smbreq,
     350             :                                         fsp,
     351             :                                         in_offset,
     352             :                                         in_data,
     353       56421 :                                         state->write_through);
     354             : 
     355       56421 :         if (NT_STATUS_IS_OK(status)) {
     356             :                 /*
     357             :                  * Doing an async write, allow this
     358             :                  * request to be canceled
     359             :                  */
     360       53267 :                 tevent_req_set_cancel_fn(req, smbd_smb2_write_cancel);
     361       53267 :                 return req;
     362             :         }
     363             : 
     364        3154 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
     365             :                 /* Real error in setting up aio. Fail. */
     366          12 :                 tevent_req_nterror(req, status);
     367          12 :                 return tevent_req_post(req, ev);
     368             :         }
     369             : 
     370             :         /* Fallback to synchronous. */
     371        3142 :         init_strict_lock_struct(fsp,
     372        3142 :                                 fsp->op->global->open_persistent_id,
     373             :                                 in_offset,
     374             :                                 in_data.length,
     375             :                                 WRITE_LOCK,
     376             :                                 lp_posix_cifsu_locktype(fsp),
     377             :                                 &lock);
     378             : 
     379        3142 :         if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &lock)) {
     380           0 :                 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
     381           0 :                 return tevent_req_post(req, ev);
     382             :         }
     383             : 
     384             :         /*
     385             :          * Note: in_data.data is NULL for the recvfile case.
     386             :          */
     387        3142 :         nwritten = write_file(smbreq, fsp,
     388        3142 :                               (const char *)in_data.data,
     389             :                               in_offset,
     390             :                               in_data.length);
     391             : 
     392        3142 :         status = smb2_write_complete(req, nwritten, errno);
     393             : 
     394        3142 :         DEBUG(10,("smb2: write on "
     395             :                 "file %s, offset %.0f, requested %u, written = %u\n",
     396             :                 fsp_str_dbg(fsp),
     397             :                 (double)in_offset,
     398             :                 (unsigned int)in_data.length,
     399             :                 (unsigned int)nwritten ));
     400             : 
     401        3142 :         if (!NT_STATUS_IS_OK(status)) {
     402        2216 :                 tevent_req_nterror(req, status);
     403             :         } else {
     404             :                 /* Success. */
     405         926 :                 tevent_req_done(req);
     406             :         }
     407             : 
     408        3142 :         return tevent_req_post(req, ev);
     409             : }
     410             : 
     411        7137 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
     412             : {
     413        7137 :         struct tevent_req *req = tevent_req_callback_data(subreq,
     414             :                                  struct tevent_req);
     415        7137 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     416             :                                               struct smbd_smb2_write_state);
     417          47 :         NTSTATUS status;
     418        7137 :         ssize_t nwritten = -1;
     419             : 
     420        7137 :         status = np_write_recv(subreq, &nwritten);
     421        7137 :         TALLOC_FREE(subreq);
     422        7137 :         if (!NT_STATUS_IS_OK(status)) {
     423           0 :                 NTSTATUS old = status;
     424           0 :                 status = nt_status_np_pipe(old);
     425           0 :                 tevent_req_nterror(req, status);
     426           0 :                 return;
     427             :         }
     428             : 
     429        7137 :         if ((nwritten == 0 && state->in_length != 0) || (nwritten < 0)) {
     430           0 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     431           0 :                 return;
     432             :         }
     433             : 
     434        7137 :         state->out_count = nwritten;
     435             : 
     436        7137 :         tevent_req_done(req);
     437             : }
     438             : 
     439       63720 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
     440             :                                      uint32_t *out_count)
     441             : {
     442          47 :         NTSTATUS status;
     443       63720 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     444             :                                               struct smbd_smb2_write_state);
     445             : 
     446       63720 :         if (tevent_req_is_nterror(req, &status)) {
     447        2424 :                 tevent_req_received(req);
     448        2424 :                 return status;
     449             :         }
     450             : 
     451       61296 :         *out_count = state->out_count;
     452             : 
     453       61296 :         tevent_req_received(req);
     454       61296 :         return NT_STATUS_OK;
     455             : }

Generated by: LCOV version 1.14