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/globals.h" 23#include "../libcli/smb/smb_common.h" 24 25static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx, 26 struct tevent_context *ev, 27 struct smbd_smb2_request *smb2req, 28 uint64_t in_file_id_volatile); 29static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req); 30 31static void smbd_smb2_request_flush_done(struct tevent_req *subreq); 32NTSTATUS smbd_smb2_request_process_flush(struct smbd_smb2_request *req) 33{ 34 const uint8_t *inhdr; 35 const uint8_t *inbody; 36 int i = req->current_idx; 37 size_t expected_body_size = 0x18; 38 size_t body_size; 39 uint64_t in_file_id_persistent; 40 uint64_t in_file_id_volatile; 41 struct tevent_req *subreq; 42 43 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; 44 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { 45 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 46 } 47 48 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 49 50 body_size = SVAL(inbody, 0x00); 51 if (body_size != expected_body_size) { 52 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 53 } 54 55 in_file_id_persistent = BVAL(inbody, 0x08); 56 in_file_id_volatile = BVAL(inbody, 0x10); 57 58 if (req->compat_chain_fsp) { 59 /* skip check */ 60 } else if (in_file_id_persistent != 0) { 61 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 62 } 63 64 subreq = smbd_smb2_flush_send(req, 65 req->sconn->smb2.event_ctx, 66 req, 67 in_file_id_volatile); 68 if (subreq == NULL) { 69 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 70 } 71 tevent_req_set_callback(subreq, smbd_smb2_request_flush_done, req); 72 73 return smbd_smb2_request_pending_queue(req, subreq); 74} 75 76static void smbd_smb2_request_flush_done(struct tevent_req *subreq) 77{ 78 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 79 struct smbd_smb2_request); 80 DATA_BLOB outbody; 81 NTSTATUS status; 82 NTSTATUS error; /* transport error */ 83 84 status = smbd_smb2_flush_recv(subreq); 85 TALLOC_FREE(subreq); 86 if (!NT_STATUS_IS_OK(status)) { 87 error = smbd_smb2_request_error(req, status); 88 if (!NT_STATUS_IS_OK(error)) { 89 smbd_server_connection_terminate(req->sconn, 90 nt_errstr(error)); 91 return; 92 } 93 return; 94 } 95 96 outbody = data_blob_talloc(req->out.vector, NULL, 0x10); 97 if (outbody.data == NULL) { 98 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 99 if (!NT_STATUS_IS_OK(error)) { 100 smbd_server_connection_terminate(req->sconn, 101 nt_errstr(error)); 102 return; 103 } 104 return; 105 } 106 107 SSVAL(outbody.data, 0x00, 0x04); /* struct size */ 108 SSVAL(outbody.data, 0x02, 0); /* reserved */ 109 110 error = smbd_smb2_request_done(req, outbody, NULL); 111 if (!NT_STATUS_IS_OK(error)) { 112 smbd_server_connection_terminate(req->sconn, 113 nt_errstr(error)); 114 return; 115 } 116} 117 118struct smbd_smb2_flush_state { 119 struct smbd_smb2_request *smb2req; 120}; 121 122static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx, 123 struct tevent_context *ev, 124 struct smbd_smb2_request *smb2req, 125 uint64_t in_file_id_volatile) 126{ 127 struct tevent_req *req; 128 struct smbd_smb2_flush_state *state; 129 NTSTATUS status; 130 struct smb_request *smbreq; 131 files_struct *fsp; 132 133 req = tevent_req_create(mem_ctx, &state, 134 struct smbd_smb2_flush_state); 135 if (req == NULL) { 136 return NULL; 137 } 138 state->smb2req = smb2req; 139 140 DEBUG(10,("smbd_smb2_flush: file_id[0x%016llX]\n", 141 (unsigned long long)in_file_id_volatile)); 142 143 smbreq = smbd_smb2_fake_smb_request(smb2req); 144 if (tevent_req_nomem(smbreq, req)) { 145 return tevent_req_post(req, ev); 146 } 147 148 if (IS_IPC(smbreq->conn)) { 149 tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED); 150 return tevent_req_post(req, ev); 151 } 152 153 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); 154 if (fsp == NULL) { 155 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 156 return tevent_req_post(req, ev); 157 } 158 if (smbreq->conn != fsp->conn) { 159 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 160 return tevent_req_post(req, ev); 161 } 162 if (smb2req->session->vuid != fsp->vuid) { 163 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 164 return tevent_req_post(req, ev); 165 } 166 167 if (!CHECK_WRITE(fsp)) { 168 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); 169 return tevent_req_post(req, ev); 170 } 171 172 status = sync_file(smbreq->conn, fsp, true); 173 if (!NT_STATUS_IS_OK(status)) { 174 DEBUG(5,("smbd_smb2_flush: sync_file for %s returned %s\n", 175 fsp_str_dbg(fsp), nt_errstr(status))); 176 tevent_req_nterror(req, status); 177 return tevent_req_post(req, ev); 178 } 179 180 tevent_req_done(req); 181 return tevent_req_post(req, ev); 182} 183 184static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req) 185{ 186 NTSTATUS status; 187 188 if (tevent_req_is_nterror(req, &status)) { 189 tevent_req_received(req); 190 return status; 191 } 192 193 tevent_req_received(req); 194 return NT_STATUS_OK; 195} 196