s3: smbd: Fix SMB2-FLUSH against directories.
[Samba.git] / source3 / smbd / smb2_flush.c
blobef9b7fddcf9dd359ca1d44f2f8bbe63f62245050
1 /*
2 Unix SMB/CIFS implementation.
3 Core SMB2 server
5 Copyright (C) Stefan Metzmacher 2009
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.
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.
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/>.
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 "libcli/security/security.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_SMB2
31 static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
32 struct tevent_context *ev,
33 struct smbd_smb2_request *smb2req,
34 struct files_struct *fsp);
35 static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req);
37 static void smbd_smb2_request_flush_done(struct tevent_req *subreq);
38 NTSTATUS smbd_smb2_request_process_flush(struct smbd_smb2_request *req)
40 NTSTATUS status;
41 const uint8_t *inbody;
42 uint64_t in_file_id_persistent;
43 uint64_t in_file_id_volatile;
44 struct files_struct *in_fsp;
45 struct tevent_req *subreq;
47 status = smbd_smb2_request_verify_sizes(req, 0x18);
48 if (!NT_STATUS_IS_OK(status)) {
49 return smbd_smb2_request_error(req, status);
51 inbody = SMBD_SMB2_IN_BODY_PTR(req);
53 in_file_id_persistent = BVAL(inbody, 0x08);
54 in_file_id_volatile = BVAL(inbody, 0x10);
56 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
57 if (in_fsp == NULL) {
58 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
61 subreq = smbd_smb2_flush_send(req, req->sconn->ev_ctx,
62 req, in_fsp);
63 if (subreq == NULL) {
64 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
66 tevent_req_set_callback(subreq, smbd_smb2_request_flush_done, req);
68 return smbd_smb2_request_pending_queue(req, subreq, 500);
71 static void smbd_smb2_request_flush_done(struct tevent_req *subreq)
73 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
74 struct smbd_smb2_request);
75 DATA_BLOB outbody;
76 NTSTATUS status;
77 NTSTATUS error; /* transport error */
79 status = smbd_smb2_flush_recv(subreq);
80 TALLOC_FREE(subreq);
81 if (!NT_STATUS_IS_OK(status)) {
82 error = smbd_smb2_request_error(req, status);
83 if (!NT_STATUS_IS_OK(error)) {
84 smbd_server_connection_terminate(req->xconn,
85 nt_errstr(error));
86 return;
88 return;
91 outbody = smbd_smb2_generate_outbody(req, 0x04);
92 if (outbody.data == NULL) {
93 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
94 if (!NT_STATUS_IS_OK(error)) {
95 smbd_server_connection_terminate(req->xconn,
96 nt_errstr(error));
97 return;
99 return;
102 SSVAL(outbody.data, 0x00, 0x04); /* struct size */
103 SSVAL(outbody.data, 0x02, 0); /* reserved */
105 error = smbd_smb2_request_done(req, outbody, NULL);
106 if (!NT_STATUS_IS_OK(error)) {
107 smbd_server_connection_terminate(req->xconn,
108 nt_errstr(error));
109 return;
113 struct smbd_smb2_flush_state {
114 struct smbd_smb2_request *smb2req;
117 static void smbd_smb2_flush_done(struct tevent_req *subreq);
119 static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
120 struct tevent_context *ev,
121 struct smbd_smb2_request *smb2req,
122 struct files_struct *fsp)
124 struct tevent_req *req;
125 struct tevent_req *subreq;
126 struct smbd_smb2_flush_state *state;
127 struct smb_request *smbreq;
128 int ret;
130 req = tevent_req_create(mem_ctx, &state,
131 struct smbd_smb2_flush_state);
132 if (req == NULL) {
133 return NULL;
135 state->smb2req = smb2req;
137 DEBUG(10,("smbd_smb2_flush: %s - %s\n",
138 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
140 smbreq = smbd_smb2_fake_smb_request(smb2req);
141 if (tevent_req_nomem(smbreq, req)) {
142 return tevent_req_post(req, ev);
145 if (IS_IPC(smbreq->conn)) {
146 tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
147 return tevent_req_post(req, ev);
150 if (!CHECK_WRITE(fsp)) {
151 bool allow_dir_flush = false;
152 uint32_t flush_access = FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY;
154 if (!fsp->is_directory) {
155 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
156 return tevent_req_post(req, ev);
160 * Directories are not writable in the conventional
161 * sense, but if opened with *either*
162 * FILE_ADD_FILE or FILE_ADD_SUBDIRECTORY
163 * they can be flushed.
166 if ((fsp->access_mask & flush_access) != 0) {
167 allow_dir_flush = true;
170 if (allow_dir_flush == false) {
171 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
172 return tevent_req_post(req, ev);
176 if (fsp->fh->fd == -1) {
177 tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
178 return tevent_req_post(req, ev);
181 if (!lp_strict_sync(SNUM(smbreq->conn))) {
183 * No strict sync. Don't really do
184 * anything here.
186 tevent_req_done(req);
187 return tevent_req_post(req, ev);
190 ret = flush_write_cache(fsp, SAMBA_SYNC_FLUSH);
191 if (ret == -1) {
192 tevent_req_nterror(req, map_nt_error_from_unix(errno));
193 return tevent_req_post(req, ev);
196 subreq = SMB_VFS_FSYNC_SEND(state, ev, fsp);
197 if (tevent_req_nomem(subreq, req)) {
198 return tevent_req_post(req, ev);
201 tevent_req_set_callback(subreq, smbd_smb2_flush_done, req);
203 /* Ensure any close request knows about this outstanding IO. */
204 if (!aio_add_req_to_fsp(fsp, req)) {
205 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
206 return tevent_req_post(req, ev);
209 increment_outstanding_aio_calls();
210 return req;
214 static void smbd_smb2_flush_done(struct tevent_req *subreq)
216 struct tevent_req *req = tevent_req_callback_data(
217 subreq, struct tevent_req);
218 int ret;
219 struct vfs_aio_state vfs_aio_state;
221 decrement_outstanding_aio_calls();
223 ret = SMB_VFS_FSYNC_RECV(subreq, &vfs_aio_state);
224 TALLOC_FREE(subreq);
225 if (ret == -1) {
226 tevent_req_nterror(req, map_nt_error_from_unix(vfs_aio_state.error));
227 return;
229 tevent_req_done(req);
232 static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req)
234 NTSTATUS status;
236 if (tevent_req_is_nterror(req, &status)) {
237 tevent_req_received(req);
238 return status;
241 tevent_req_received(req);
242 return NT_STATUS_OK;