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 "libcli/security/security.h"
27 :
28 : #undef DBGC_CLASS
29 : #define DBGC_CLASS DBGC_SMB2
30 :
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);
36 :
37 : static void smbd_smb2_request_flush_done(struct tevent_req *subreq);
38 0 : NTSTATUS smbd_smb2_request_process_flush(struct smbd_smb2_request *req)
39 : {
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;
46 :
47 0 : status = smbd_smb2_request_verify_sizes(req, 0x18);
48 0 : if (!NT_STATUS_IS_OK(status)) {
49 0 : return smbd_smb2_request_error(req, status);
50 : }
51 0 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
52 :
53 0 : in_file_id_persistent = BVAL(inbody, 0x08);
54 0 : in_file_id_volatile = BVAL(inbody, 0x10);
55 :
56 0 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
57 0 : if (in_fsp == NULL) {
58 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
59 : }
60 :
61 0 : subreq = smbd_smb2_flush_send(req, req->sconn->ev_ctx,
62 : req, in_fsp);
63 0 : if (subreq == NULL) {
64 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
65 : }
66 0 : tevent_req_set_callback(subreq, smbd_smb2_request_flush_done, req);
67 :
68 0 : return smbd_smb2_request_pending_queue(req, subreq, 500);
69 : }
70 :
71 0 : static void smbd_smb2_request_flush_done(struct tevent_req *subreq)
72 : {
73 0 : 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 */
78 :
79 0 : status = smbd_smb2_flush_recv(subreq);
80 0 : TALLOC_FREE(subreq);
81 0 : if (!NT_STATUS_IS_OK(status)) {
82 0 : error = smbd_smb2_request_error(req, status);
83 0 : if (!NT_STATUS_IS_OK(error)) {
84 0 : smbd_server_connection_terminate(req->xconn,
85 : nt_errstr(error));
86 0 : return;
87 : }
88 0 : return;
89 : }
90 :
91 0 : outbody = smbd_smb2_generate_outbody(req, 0x04);
92 0 : if (outbody.data == NULL) {
93 0 : error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
94 0 : if (!NT_STATUS_IS_OK(error)) {
95 0 : smbd_server_connection_terminate(req->xconn,
96 : nt_errstr(error));
97 0 : return;
98 : }
99 0 : return;
100 : }
101 :
102 0 : SSVAL(outbody.data, 0x00, 0x04); /* struct size */
103 0 : SSVAL(outbody.data, 0x02, 0); /* reserved */
104 :
105 0 : error = smbd_smb2_request_done(req, outbody, NULL);
106 0 : if (!NT_STATUS_IS_OK(error)) {
107 0 : smbd_server_connection_terminate(req->xconn,
108 : nt_errstr(error));
109 0 : return;
110 : }
111 : }
112 :
113 : struct smbd_smb2_flush_state {
114 : struct smbd_smb2_request *smb2req;
115 : struct files_struct *fsp;
116 : };
117 :
118 : static void smbd_smb2_flush_done(struct tevent_req *subreq);
119 :
120 0 : static struct tevent_req *smbd_smb2_flush_send(TALLOC_CTX *mem_ctx,
121 : struct tevent_context *ev,
122 : struct smbd_smb2_request *smb2req,
123 : struct files_struct *fsp)
124 : {
125 : struct tevent_req *req;
126 : struct tevent_req *subreq;
127 : struct smbd_smb2_flush_state *state;
128 : struct smb_request *smbreq;
129 0 : bool is_compound = false;
130 0 : bool is_last_in_compound = false;
131 :
132 0 : req = tevent_req_create(mem_ctx, &state,
133 : struct smbd_smb2_flush_state);
134 0 : if (req == NULL) {
135 0 : return NULL;
136 : }
137 0 : state->smb2req = smb2req;
138 0 : state->fsp = fsp;
139 :
140 0 : DEBUG(10,("smbd_smb2_flush: %s - %s\n",
141 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
142 :
143 0 : smbreq = smbd_smb2_fake_smb_request(smb2req);
144 0 : if (tevent_req_nomem(smbreq, req)) {
145 0 : return tevent_req_post(req, ev);
146 : }
147 :
148 0 : if (IS_IPC(smbreq->conn)) {
149 0 : tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
150 0 : return tevent_req_post(req, ev);
151 : }
152 :
153 0 : if (!CHECK_WRITE(fsp)) {
154 0 : bool allow_dir_flush = false;
155 0 : uint32_t flush_access = FILE_ADD_FILE | FILE_ADD_SUBDIRECTORY;
156 :
157 0 : if (!fsp->fsp_flags.is_directory) {
158 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
159 0 : return tevent_req_post(req, ev);
160 : }
161 :
162 : /*
163 : * Directories are not writable in the conventional
164 : * sense, but if opened with *either*
165 : * FILE_ADD_FILE or FILE_ADD_SUBDIRECTORY
166 : * they can be flushed.
167 : */
168 :
169 0 : if ((fsp->access_mask & flush_access) != 0) {
170 0 : allow_dir_flush = true;
171 : }
172 :
173 0 : if (allow_dir_flush == false) {
174 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
175 0 : return tevent_req_post(req, ev);
176 : }
177 : }
178 :
179 0 : if (fsp_get_io_fd(fsp) == -1) {
180 0 : tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE);
181 0 : return tevent_req_post(req, ev);
182 : }
183 :
184 0 : if (!lp_strict_sync(SNUM(smbreq->conn))) {
185 : /*
186 : * No strict sync. Don't really do
187 : * anything here.
188 : */
189 0 : tevent_req_done(req);
190 0 : return tevent_req_post(req, ev);
191 : }
192 :
193 0 : subreq = SMB_VFS_FSYNC_SEND(state, ev, fsp);
194 0 : if (tevent_req_nomem(subreq, req)) {
195 0 : return tevent_req_post(req, ev);
196 : }
197 :
198 0 : tevent_req_set_callback(subreq, smbd_smb2_flush_done, req);
199 :
200 0 : is_compound = smbd_smb2_is_compound(smb2req);
201 0 : is_last_in_compound = smbd_smb2_is_last_in_compound(smb2req);
202 :
203 0 : if (is_compound && !is_last_in_compound) {
204 : /*
205 : * Can't go async if we're not the
206 : * last request in a compound request.
207 : * Cause this request to complete synchronously.
208 : */
209 0 : smb2_request_set_async_internal(state->smb2req, true);
210 : }
211 :
212 : /* Ensure any close request knows about this outstanding IO. */
213 0 : if (!aio_add_req_to_fsp(fsp, req)) {
214 0 : tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
215 0 : return tevent_req_post(req, ev);
216 : }
217 :
218 0 : return req;
219 :
220 : }
221 :
222 0 : static void smbd_smb2_flush_done(struct tevent_req *subreq)
223 : {
224 0 : struct tevent_req *req = tevent_req_callback_data(
225 : subreq, struct tevent_req);
226 0 : struct smbd_smb2_flush_state *state = tevent_req_data(
227 : req, struct smbd_smb2_flush_state);
228 : int ret;
229 : struct vfs_aio_state vfs_aio_state;
230 :
231 0 : ret = SMB_VFS_FSYNC_RECV(subreq, &vfs_aio_state);
232 0 : TALLOC_FREE(subreq);
233 0 : if (ret == -1) {
234 0 : tevent_req_nterror(req, map_nt_error_from_unix(vfs_aio_state.error));
235 0 : return;
236 : }
237 0 : if (state->fsp->fsp_flags.modified) {
238 0 : trigger_write_time_update_immediate(state->fsp);
239 : }
240 0 : tevent_req_done(req);
241 : }
242 :
243 0 : static NTSTATUS smbd_smb2_flush_recv(struct tevent_req *req)
244 : {
245 : NTSTATUS status;
246 :
247 0 : if (tevent_req_is_nterror(req, &status)) {
248 0 : tevent_req_received(req);
249 0 : return status;
250 : }
251 :
252 0 : tevent_req_received(req);
253 0 : return NT_STATUS_OK;
254 : }
|