LCOV - code coverage report
Current view: top level - source3/smbd - smb2_write.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 162 205 79.0 %
Date: 2024-04-21 15:09:00 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             : #include "libcli/security/security.h"
      28             : 
      29             : #undef DBGC_CLASS
      30             : #define DBGC_CLASS DBGC_SMB2
      31             : 
      32             : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
      33             :                                                struct tevent_context *ev,
      34             :                                                struct smbd_smb2_request *smb2req,
      35             :                                                struct files_struct *in_fsp,
      36             :                                                DATA_BLOB in_data,
      37             :                                                uint64_t in_offset,
      38             :                                                uint32_t in_flags);
      39             : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
      40             :                                      uint32_t *out_count);
      41             : 
      42             : static void smbd_smb2_request_write_done(struct tevent_req *subreq);
      43       63750 : NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
      44             : {
      45       63750 :         struct smbXsrv_connection *xconn = req->xconn;
      46          48 :         NTSTATUS status;
      47          48 :         const uint8_t *inbody;
      48          48 :         uint16_t in_data_offset;
      49          48 :         uint32_t in_data_length;
      50          48 :         DATA_BLOB in_data_buffer;
      51          48 :         uint64_t in_offset;
      52          48 :         uint64_t in_file_id_persistent;
      53          48 :         uint64_t in_file_id_volatile;
      54          48 :         struct files_struct *in_fsp;
      55          48 :         uint32_t in_flags;
      56       63750 :         size_t in_dyn_len = 0;
      57       63750 :         uint8_t *in_dyn_ptr = NULL;
      58          48 :         struct tevent_req *subreq;
      59             : 
      60       63750 :         status = smbd_smb2_request_verify_sizes(req, 0x31);
      61       63750 :         if (!NT_STATUS_IS_OK(status)) {
      62           0 :                 return smbd_smb2_request_error(req, status);
      63             :         }
      64       63750 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
      65             : 
      66       63750 :         in_data_offset          = SVAL(inbody, 0x02);
      67       63750 :         in_data_length          = IVAL(inbody, 0x04);
      68       63750 :         in_offset               = BVAL(inbody, 0x08);
      69       63750 :         in_file_id_persistent   = BVAL(inbody, 0x10);
      70       63750 :         in_file_id_volatile     = BVAL(inbody, 0x18);
      71       63750 :         in_flags                = IVAL(inbody, 0x2C);
      72             : 
      73       63750 :         if (in_data_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
      74           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      75             :         }
      76             : 
      77       63750 :         if (req->smb1req != NULL && req->smb1req->unread_bytes > 0) {
      78           0 :                 in_dyn_ptr = NULL;
      79           0 :                 in_dyn_len = req->smb1req->unread_bytes;
      80             :         } else {
      81       63750 :                 in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req);
      82       63750 :                 in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req);
      83             :         }
      84             : 
      85       63750 :         if (in_data_length > in_dyn_len) {
      86           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      87             :         }
      88             : 
      89             :         /* check the max write size */
      90       63750 :         if (in_data_length > xconn->smb2.server.max_write) {
      91           0 :                 DEBUG(2,("smbd_smb2_request_process_write : "
      92             :                         "client ignored max write :%s: 0x%08X: 0x%08X\n",
      93             :                         __location__, in_data_length, xconn->smb2.server.max_write));
      94           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      95             :         }
      96             : 
      97             :         /*
      98             :          * Note: that in_dyn_ptr is NULL for the recvfile case.
      99             :          */
     100       63750 :         in_data_buffer.data = in_dyn_ptr;
     101       63750 :         in_data_buffer.length = in_data_length;
     102             : 
     103       63750 :         status = smbd_smb2_request_verify_creditcharge(req, in_data_length);
     104       63750 :         if (!NT_STATUS_IS_OK(status)) {
     105           0 :                 return smbd_smb2_request_error(req, status);
     106             :         }
     107             : 
     108       63750 :         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
     109       63750 :         if (in_fsp == NULL) {
     110           0 :                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
     111             :         }
     112             : 
     113       63750 :         subreq = smbd_smb2_write_send(req, req->sconn->ev_ctx,
     114             :                                       req, in_fsp,
     115             :                                       in_data_buffer,
     116             :                                       in_offset,
     117             :                                       in_flags);
     118       63750 :         if (subreq == NULL) {
     119           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     120             :         }
     121       63750 :         tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
     122             : 
     123       63750 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
     124             : }
     125             : 
     126       63746 : static void smbd_smb2_request_write_done(struct tevent_req *subreq)
     127             : {
     128       63746 :         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
     129             :                                         struct smbd_smb2_request);
     130          48 :         DATA_BLOB outbody;
     131          48 :         DATA_BLOB outdyn;
     132       63746 :         uint32_t out_count = 0;
     133          48 :         NTSTATUS status;
     134          48 :         NTSTATUS error; /* transport error */
     135             : 
     136       63746 :         status = smbd_smb2_write_recv(subreq, &out_count);
     137       63746 :         TALLOC_FREE(subreq);
     138       63746 :         if (!NT_STATUS_IS_OK(status)) {
     139        2423 :                 error = smbd_smb2_request_error(req, status);
     140        2423 :                 if (!NT_STATUS_IS_OK(error)) {
     141           0 :                         smbd_server_connection_terminate(req->xconn,
     142             :                                                          nt_errstr(error));
     143        2423 :                         return;
     144             :                 }
     145        2423 :                 return;
     146             :         }
     147             : 
     148       61323 :         outbody = smbd_smb2_generate_outbody(req, 0x10);
     149       61323 :         if (outbody.data == NULL) {
     150           0 :                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     151           0 :                 if (!NT_STATUS_IS_OK(error)) {
     152           0 :                         smbd_server_connection_terminate(req->xconn,
     153             :                                                          nt_errstr(error));
     154           0 :                         return;
     155             :                 }
     156           0 :                 return;
     157             :         }
     158             : 
     159       61323 :         SSVAL(outbody.data, 0x00, 0x10 + 1);    /* struct size */
     160       61323 :         SSVAL(outbody.data, 0x02, 0);           /* reserved */
     161       61323 :         SIVAL(outbody.data, 0x04, out_count);   /* count */
     162       61323 :         SIVAL(outbody.data, 0x08, 0);           /* remaining */
     163       61323 :         SSVAL(outbody.data, 0x0C, 0);           /* write channel info offset */
     164       61323 :         SSVAL(outbody.data, 0x0E, 0);           /* write channel info length */
     165             : 
     166       61323 :         outdyn = data_blob_const(NULL, 0);
     167             : 
     168       61323 :         error = smbd_smb2_request_done(req, outbody, &outdyn);
     169       61323 :         if (!NT_STATUS_IS_OK(error)) {
     170           0 :                 smbd_server_connection_terminate(req->xconn, nt_errstr(error));
     171           0 :                 return;
     172             :         }
     173             : }
     174             : 
     175             : struct smbd_smb2_write_state {
     176             :         struct smbd_smb2_request *smb2req;
     177             :         struct smb_request *smbreq;
     178             :         files_struct *fsp;
     179             :         bool write_through;
     180             :         uint32_t in_length;
     181             :         uint64_t in_offset;
     182             :         uint32_t out_count;
     183             : };
     184             : 
     185             : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq);
     186             : 
     187       56410 : static NTSTATUS smb2_write_complete_internal(struct tevent_req *req,
     188             :                                              ssize_t nwritten, int err,
     189             :                                              bool do_sync)
     190             : {
     191           0 :         NTSTATUS status;
     192       56410 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     193             :                                         struct smbd_smb2_write_state);
     194       56410 :         files_struct *fsp = state->fsp;
     195             : 
     196       56410 :         if (nwritten == -1) {
     197        2244 :                 if (err == EOVERFLOW && fsp_is_alternate_stream(fsp)) {
     198           0 :                         status = NT_STATUS_FILE_SYSTEM_LIMITATION;
     199             :                 } else {
     200        2244 :                         status = map_nt_error_from_unix(err);
     201             :                 }
     202             : 
     203        2244 :                 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        2244 :                 return status;
     212             :         }
     213             : 
     214       54166 :         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       54166 :         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       54166 :         if (do_sync) {
     229         926 :                 status = sync_file(fsp->conn, fsp, state->write_through);
     230         926 :                 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       54166 :         state->out_count = nwritten;
     239             : 
     240       54166 :         return NT_STATUS_OK;
     241             : }
     242             : 
     243        3140 : NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
     244             : {
     245        3140 :         return smb2_write_complete_internal(req, nwritten, err, true);
     246             : }
     247             : 
     248       53270 : NTSTATUS smb2_write_complete_nosync(struct tevent_req *req, ssize_t nwritten,
     249             :                                     int err)
     250             : {
     251       53270 :         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       63750 : 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          48 :         NTSTATUS status;
     273       63750 :         struct tevent_req *req = NULL;
     274       63750 :         struct smbd_smb2_write_state *state = NULL;
     275       63750 :         struct smb_request *smbreq = NULL;
     276       63750 :         connection_struct *conn = smb2req->tcon->compat;
     277          48 :         ssize_t nwritten;
     278          48 :         struct lock_struct lock;
     279             : 
     280       63750 :         req = tevent_req_create(mem_ctx, &state,
     281             :                                 struct smbd_smb2_write_state);
     282       63750 :         if (req == NULL) {
     283           0 :                 return NULL;
     284             :         }
     285       63750 :         state->smb2req = smb2req;
     286       63750 :         if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
     287       58926 :                 if (in_flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) {
     288           2 :                         state->write_through = true;
     289             :                 }
     290             :         }
     291       63750 :         if (in_flags & SMB2_WRITEFLAG_WRITE_THROUGH) {
     292           2 :                 state->write_through = true;
     293             :         }
     294       63750 :         state->in_length = in_data.length;
     295       63750 :         state->in_offset = in_offset;
     296       63750 :         state->out_count = 0;
     297             : 
     298       63750 :         DEBUG(10,("smbd_smb2_write: %s - %s\n",
     299             :                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
     300             : 
     301       63750 :         smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
     302       63750 :         if (tevent_req_nomem(smbreq, req)) {
     303           0 :                 return tevent_req_post(req, ev);
     304             :         }
     305       63750 :         state->smbreq = smbreq;
     306             : 
     307       63750 :         state->fsp = fsp;
     308             : 
     309       63750 :         if (IS_IPC(smbreq->conn)) {
     310        7157 :                 struct tevent_req *subreq = NULL;
     311          48 :                 bool ok;
     312             : 
     313        7157 :                 if (!fsp_is_np(fsp)) {
     314           0 :                         tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
     315           0 :                         return tevent_req_post(req, ev);
     316             :                 }
     317             : 
     318        7157 :                 subreq = np_write_send(state, ev,
     319             :                                        fsp->fake_file_handle,
     320        7109 :                                        in_data.data,
     321             :                                        in_data.length);
     322        7157 :                 if (tevent_req_nomem(subreq, req)) {
     323           0 :                         return tevent_req_post(req, ev);
     324             :                 }
     325        7157 :                 tevent_req_set_callback(subreq,
     326             :                                         smbd_smb2_write_pipe_done,
     327             :                                         req);
     328             : 
     329             :                 /*
     330             :                  * Make sure we mark the fsp as having outstanding async
     331             :                  * activity so we don't crash on shutdown close.
     332             :                  */
     333             : 
     334        7157 :                 ok = aio_add_req_to_fsp(fsp, req);
     335        7157 :                 if (!ok) {
     336           0 :                         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
     337           0 :                         return tevent_req_post(req, ev);
     338             :                 }
     339             : 
     340        7109 :                 return req;
     341             :         }
     342             : 
     343       56593 :         status = check_any_access_fsp(fsp, FILE_WRITE_DATA|FILE_APPEND_DATA);
     344       56593 :         if (!NT_STATUS_IS_OK(status)) {
     345         167 :                 tevent_req_nterror(req, status);
     346         167 :                 return tevent_req_post(req, ev);
     347             :         }
     348             : 
     349             :         /* Try and do an asynchronous write. */
     350       56426 :         status = schedule_aio_smb2_write(conn,
     351             :                                         smbreq,
     352             :                                         fsp,
     353             :                                         in_offset,
     354             :                                         in_data,
     355       56426 :                                         state->write_through);
     356             : 
     357       56426 :         if (NT_STATUS_IS_OK(status)) {
     358             :                 /*
     359             :                  * Doing an async write, allow this
     360             :                  * request to be canceled
     361             :                  */
     362       53274 :                 tevent_req_set_cancel_fn(req, smbd_smb2_write_cancel);
     363       53274 :                 return req;
     364             :         }
     365             : 
     366        3152 :         if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
     367             :                 /* Real error in setting up aio. Fail. */
     368          12 :                 tevent_req_nterror(req, status);
     369          12 :                 return tevent_req_post(req, ev);
     370             :         }
     371             : 
     372             :         /* Fallback to synchronous. */
     373        3140 :         init_strict_lock_struct(fsp,
     374        3140 :                                 fsp->op->global->open_persistent_id,
     375             :                                 in_offset,
     376             :                                 in_data.length,
     377             :                                 WRITE_LOCK,
     378             :                                 lp_posix_cifsu_locktype(fsp),
     379             :                                 &lock);
     380             : 
     381        3140 :         if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &lock)) {
     382           0 :                 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
     383           0 :                 return tevent_req_post(req, ev);
     384             :         }
     385             : 
     386             :         /*
     387             :          * Note: in_data.data is NULL for the recvfile case.
     388             :          */
     389        3140 :         nwritten = write_file(smbreq, fsp,
     390        3140 :                               (const char *)in_data.data,
     391             :                               in_offset,
     392             :                               in_data.length);
     393             : 
     394        3140 :         status = smb2_write_complete(req, nwritten, errno);
     395             : 
     396        3140 :         DEBUG(10,("smb2: write on "
     397             :                 "file %s, offset %.0f, requested %u, written = %u\n",
     398             :                 fsp_str_dbg(fsp),
     399             :                 (double)in_offset,
     400             :                 (unsigned int)in_data.length,
     401             :                 (unsigned int)nwritten ));
     402             : 
     403        3140 :         if (!NT_STATUS_IS_OK(status)) {
     404        2214 :                 tevent_req_nterror(req, status);
     405             :         } else {
     406             :                 /* Success. */
     407         926 :                 tevent_req_done(req);
     408             :         }
     409             : 
     410        3140 :         return tevent_req_post(req, ev);
     411             : }
     412             : 
     413        7157 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
     414             : {
     415        7157 :         struct tevent_req *req = tevent_req_callback_data(subreq,
     416             :                                  struct tevent_req);
     417        7157 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     418             :                                               struct smbd_smb2_write_state);
     419          48 :         NTSTATUS status;
     420        7157 :         ssize_t nwritten = -1;
     421             : 
     422        7157 :         status = np_write_recv(subreq, &nwritten);
     423        7157 :         TALLOC_FREE(subreq);
     424        7157 :         if (!NT_STATUS_IS_OK(status)) {
     425           0 :                 NTSTATUS old = status;
     426           0 :                 status = nt_status_np_pipe(old);
     427           0 :                 tevent_req_nterror(req, status);
     428           0 :                 return;
     429             :         }
     430             : 
     431        7157 :         if ((nwritten == 0 && state->in_length != 0) || (nwritten < 0)) {
     432           0 :                 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
     433           0 :                 return;
     434             :         }
     435             : 
     436        7157 :         state->out_count = nwritten;
     437             : 
     438        7157 :         tevent_req_done(req);
     439             : }
     440             : 
     441       63746 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
     442             :                                      uint32_t *out_count)
     443             : {
     444          48 :         NTSTATUS status;
     445       63746 :         struct smbd_smb2_write_state *state = tevent_req_data(req,
     446             :                                               struct smbd_smb2_write_state);
     447             : 
     448       63746 :         if (tevent_req_is_nterror(req, &status)) {
     449        2423 :                 tevent_req_received(req);
     450        2423 :                 return status;
     451             :         }
     452             : 
     453       61323 :         *out_count = state->out_count;
     454             : 
     455       61323 :         tevent_req_received(req);
     456       61323 :         return NT_STATUS_OK;
     457             : }

Generated by: LCOV version 1.14