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 :
27 : #undef DBGC_CLASS
28 : #define DBGC_CLASS DBGC_SMB2
29 :
30 : static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
31 : struct tevent_context *ev,
32 : struct smbd_smb2_request *smb2req,
33 : struct files_struct *in_fsp,
34 : uint16_t in_flags);
35 : static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
36 : uint16_t *out_flags,
37 : struct timespec *out_creation_ts,
38 : struct timespec *out_last_access_ts,
39 : struct timespec *out_last_write_ts,
40 : struct timespec *out_change_ts,
41 : uint64_t *out_allocation_size,
42 : uint64_t *out_end_of_file,
43 : uint32_t *out_file_attributes);
44 :
45 : static void smbd_smb2_request_close_done(struct tevent_req *subreq);
46 :
47 15148 : NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req)
48 : {
49 : const uint8_t *inbody;
50 : uint16_t in_flags;
51 : uint64_t in_file_id_persistent;
52 : uint64_t in_file_id_volatile;
53 : struct files_struct *in_fsp;
54 : NTSTATUS status;
55 : struct tevent_req *subreq;
56 :
57 15148 : status = smbd_smb2_request_verify_sizes(req, 0x18);
58 15148 : if (!NT_STATUS_IS_OK(status)) {
59 0 : return smbd_smb2_request_error(req, status);
60 : }
61 15148 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
62 :
63 15148 : in_flags = SVAL(inbody, 0x02);
64 15148 : in_file_id_persistent = BVAL(inbody, 0x08);
65 15148 : in_file_id_volatile = BVAL(inbody, 0x10);
66 :
67 15148 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
68 15148 : if (in_fsp == NULL) {
69 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
70 : }
71 :
72 15148 : subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx,
73 : req, in_fsp, in_flags);
74 15148 : if (subreq == NULL) {
75 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
76 : }
77 15148 : tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
78 :
79 15148 : return smbd_smb2_request_pending_queue(req, subreq, 500);
80 : }
81 :
82 15148 : static void smbd_smb2_request_close_done(struct tevent_req *subreq)
83 : {
84 13142 : struct smbd_smb2_request *req =
85 15148 : tevent_req_callback_data(subreq,
86 : struct smbd_smb2_request);
87 : DATA_BLOB outbody;
88 15148 : uint16_t out_flags = 0;
89 15148 : connection_struct *conn = req->tcon->compat;
90 15148 : struct timespec out_creation_ts = { 0, };
91 15148 : struct timespec out_last_access_ts = { 0, };
92 15148 : struct timespec out_last_write_ts = { 0, };
93 15148 : struct timespec out_change_ts = { 0, };
94 15148 : uint64_t out_allocation_size = 0;
95 15148 : uint64_t out_end_of_file = 0;
96 15148 : uint32_t out_file_attributes = 0;
97 : NTSTATUS status;
98 : NTSTATUS error;
99 :
100 15148 : status = smbd_smb2_close_recv(subreq,
101 : &out_flags,
102 : &out_creation_ts,
103 : &out_last_access_ts,
104 : &out_last_write_ts,
105 : &out_change_ts,
106 : &out_allocation_size,
107 : &out_end_of_file,
108 : &out_file_attributes);
109 15148 : TALLOC_FREE(subreq);
110 15148 : if (!NT_STATUS_IS_OK(status)) {
111 0 : error = smbd_smb2_request_error(req, status);
112 0 : if (!NT_STATUS_IS_OK(error)) {
113 0 : smbd_server_connection_terminate(req->xconn,
114 : nt_errstr(error));
115 0 : return;
116 : }
117 0 : return;
118 : }
119 :
120 15148 : outbody = smbd_smb2_generate_outbody(req, 0x3C);
121 15148 : if (outbody.data == NULL) {
122 0 : error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
123 0 : if (!NT_STATUS_IS_OK(error)) {
124 0 : smbd_server_connection_terminate(req->xconn,
125 : nt_errstr(error));
126 0 : return;
127 : }
128 0 : return;
129 : }
130 :
131 15148 : SSVAL(outbody.data, 0x00, 0x3C); /* struct size */
132 15148 : SSVAL(outbody.data, 0x02, out_flags);
133 15148 : SIVAL(outbody.data, 0x04, 0); /* reserved */
134 15148 : put_long_date_full_timespec(conn->ts_res,
135 15148 : (char *)outbody.data + 0x08, &out_creation_ts);
136 15148 : put_long_date_full_timespec(conn->ts_res,
137 15148 : (char *)outbody.data + 0x10, &out_last_access_ts);
138 15148 : put_long_date_full_timespec(conn->ts_res,
139 15148 : (char *)outbody.data + 0x18, &out_last_write_ts);
140 15148 : put_long_date_full_timespec(conn->ts_res,
141 15148 : (char *)outbody.data + 0x20, &out_change_ts);
142 15148 : SBVAL(outbody.data, 0x28, out_allocation_size);
143 15148 : SBVAL(outbody.data, 0x30, out_end_of_file);
144 15148 : SIVAL(outbody.data, 0x38, out_file_attributes);
145 :
146 15148 : error = smbd_smb2_request_done(req, outbody, NULL);
147 15148 : if (!NT_STATUS_IS_OK(error)) {
148 2490 : smbd_server_connection_terminate(req->xconn,
149 : nt_errstr(error));
150 0 : return;
151 : }
152 : }
153 :
154 0 : static void setup_close_full_information(connection_struct *conn,
155 : struct smb_filename *smb_fname,
156 : struct timespec *out_creation_ts,
157 : struct timespec *out_last_access_ts,
158 : struct timespec *out_last_write_ts,
159 : struct timespec *out_change_ts,
160 : uint16_t *out_flags,
161 : uint64_t *out_allocation_size,
162 : uint64_t *out_end_of_file)
163 : {
164 0 : *out_flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
165 0 : *out_last_write_ts = smb_fname->st.st_ex_mtime;
166 0 : *out_last_access_ts = smb_fname->st.st_ex_atime;
167 0 : *out_creation_ts = get_create_timespec(conn, NULL, smb_fname);
168 0 : *out_change_ts = get_change_timespec(conn, NULL, smb_fname);
169 :
170 0 : if (lp_dos_filetime_resolution(SNUM(conn))) {
171 0 : dos_filetime_timespec(out_creation_ts);
172 0 : dos_filetime_timespec(out_last_write_ts);
173 0 : dos_filetime_timespec(out_last_access_ts);
174 0 : dos_filetime_timespec(out_change_ts);
175 : }
176 0 : if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
177 0 : *out_end_of_file = get_file_size_stat(&smb_fname->st);
178 : }
179 :
180 0 : *out_allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
181 0 : }
182 :
183 15148 : static NTSTATUS smbd_smb2_close(struct smbd_smb2_request *req,
184 : struct files_struct **_fsp,
185 : uint16_t in_flags,
186 : uint16_t *out_flags,
187 : struct timespec *out_creation_ts,
188 : struct timespec *out_last_access_ts,
189 : struct timespec *out_last_write_ts,
190 : struct timespec *out_change_ts,
191 : uint64_t *out_allocation_size,
192 : uint64_t *out_end_of_file,
193 : uint32_t *out_file_attributes)
194 : {
195 : NTSTATUS status;
196 : struct smb_request *smbreq;
197 15148 : connection_struct *conn = req->tcon->compat;
198 15148 : struct files_struct *fsp = *_fsp;
199 15148 : struct smb_filename *smb_fname = NULL;
200 :
201 15148 : *out_creation_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
202 15148 : *out_last_access_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
203 15148 : *out_last_write_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
204 15148 : *out_change_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
205 :
206 15148 : *out_flags = 0;
207 15148 : *out_allocation_size = 0;
208 15148 : *out_end_of_file = 0;
209 15148 : *out_file_attributes = 0;
210 :
211 15148 : DEBUG(10,("smbd_smb2_close: %s - %s\n",
212 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
213 :
214 15148 : smbreq = smbd_smb2_fake_smb_request(req);
215 15148 : if (smbreq == NULL) {
216 0 : return NT_STATUS_NO_MEMORY;
217 : }
218 :
219 15148 : if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
220 0 : *out_file_attributes = fdos_mode(fsp);
221 0 : fsp->fsp_flags.fstat_before_close = true;
222 : }
223 :
224 15148 : status = close_file_smb(smbreq, fsp, NORMAL_CLOSE);
225 15148 : if (!NT_STATUS_IS_OK(status)) {
226 0 : DEBUG(5,("smbd_smb2_close: close_file[%s]: %s\n",
227 : smb_fname_str_dbg(smb_fname), nt_errstr(status)));
228 0 : file_free(smbreq, fsp);
229 0 : *_fsp = fsp = NULL;
230 0 : return status;
231 : }
232 :
233 15148 : if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
234 0 : setup_close_full_information(conn,
235 : fsp->fsp_name,
236 : out_creation_ts,
237 : out_last_access_ts,
238 : out_last_write_ts,
239 : out_change_ts,
240 : out_flags,
241 : out_allocation_size,
242 : out_end_of_file);
243 : }
244 :
245 15148 : file_free(smbreq, fsp);
246 15148 : *_fsp = fsp = NULL;
247 15148 : return NT_STATUS_OK;
248 : }
249 :
250 : struct smbd_smb2_close_state {
251 : struct smbd_smb2_request *smb2req;
252 : struct files_struct *in_fsp;
253 : uint16_t in_flags;
254 : uint16_t out_flags;
255 : struct timespec out_creation_ts;
256 : struct timespec out_last_access_ts;
257 : struct timespec out_last_write_ts;
258 : struct timespec out_change_ts;
259 : uint64_t out_allocation_size;
260 : uint64_t out_end_of_file;
261 : uint32_t out_file_attributes;
262 : struct tevent_queue *wait_queue;
263 : };
264 :
265 : static void smbd_smb2_close_wait_done(struct tevent_req *subreq);
266 :
267 15148 : static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
268 : struct tevent_context *ev,
269 : struct smbd_smb2_request *smb2req,
270 : struct files_struct *in_fsp,
271 : uint16_t in_flags)
272 : {
273 : struct tevent_req *req;
274 : struct smbd_smb2_close_state *state;
275 : unsigned i;
276 : NTSTATUS status;
277 :
278 15148 : req = tevent_req_create(mem_ctx, &state,
279 : struct smbd_smb2_close_state);
280 15148 : if (req == NULL) {
281 0 : return NULL;
282 : }
283 15148 : state->smb2req = smb2req;
284 15148 : state->in_fsp = in_fsp;
285 15148 : state->in_flags = in_flags;
286 :
287 15148 : in_fsp->fsp_flags.closing = true;
288 :
289 15148 : i = 0;
290 28290 : while (i < in_fsp->num_aio_requests) {
291 0 : bool ok = tevent_req_cancel(in_fsp->aio_requests[i]);
292 0 : if (ok) {
293 0 : continue;
294 : }
295 0 : i += 1;
296 : }
297 :
298 15148 : if (in_fsp->num_aio_requests != 0) {
299 : struct tevent_req *subreq;
300 :
301 0 : state->wait_queue = tevent_queue_create(state,
302 : "smbd_smb2_close_send_wait_queue");
303 0 : if (tevent_req_nomem(state->wait_queue, req)) {
304 0 : return tevent_req_post(req, ev);
305 : }
306 : /*
307 : * Now wait until all aio requests on this fsp are
308 : * finished.
309 : *
310 : * We don't set a callback, as we just want to block the
311 : * wait queue and the talloc_free() of fsp->aio_request
312 : * will remove the item from the wait queue.
313 : */
314 0 : subreq = tevent_queue_wait_send(in_fsp->aio_requests,
315 0 : smb2req->sconn->ev_ctx,
316 0 : state->wait_queue);
317 0 : if (tevent_req_nomem(subreq, req)) {
318 0 : return tevent_req_post(req, ev);
319 : }
320 :
321 : /*
322 : * Now we add our own waiter to the end of the queue,
323 : * this way we get notified when all pending requests are
324 : * finished.
325 : */
326 0 : subreq = tevent_queue_wait_send(state,
327 0 : smb2req->sconn->ev_ctx,
328 0 : state->wait_queue);
329 0 : if (tevent_req_nomem(subreq, req)) {
330 0 : return tevent_req_post(req, ev);
331 : }
332 :
333 0 : tevent_req_set_callback(subreq, smbd_smb2_close_wait_done, req);
334 0 : return req;
335 : }
336 :
337 133426 : status = smbd_smb2_close(smb2req,
338 15148 : &state->in_fsp,
339 15148 : state->in_flags,
340 15148 : &state->out_flags,
341 15148 : &state->out_creation_ts,
342 15148 : &state->out_last_access_ts,
343 15148 : &state->out_last_write_ts,
344 15148 : &state->out_change_ts,
345 15148 : &state->out_allocation_size,
346 15148 : &state->out_end_of_file,
347 15148 : &state->out_file_attributes);
348 15148 : if (tevent_req_nterror(req, status)) {
349 0 : return tevent_req_post(req, ev);
350 : }
351 :
352 15148 : tevent_req_done(req);
353 15148 : return tevent_req_post(req, ev);
354 : }
355 :
356 0 : static void smbd_smb2_close_wait_done(struct tevent_req *subreq)
357 : {
358 0 : struct tevent_req *req = tevent_req_callback_data(
359 : subreq, struct tevent_req);
360 0 : struct smbd_smb2_close_state *state = tevent_req_data(
361 : req, struct smbd_smb2_close_state);
362 : NTSTATUS status;
363 :
364 0 : tevent_queue_wait_recv(subreq);
365 0 : TALLOC_FREE(subreq);
366 :
367 0 : status = smbd_smb2_close(state->smb2req,
368 : &state->in_fsp,
369 0 : state->in_flags,
370 : &state->out_flags,
371 : &state->out_creation_ts,
372 : &state->out_last_access_ts,
373 : &state->out_last_write_ts,
374 : &state->out_change_ts,
375 : &state->out_allocation_size,
376 : &state->out_end_of_file,
377 : &state->out_file_attributes);
378 0 : if (tevent_req_nterror(req, status)) {
379 0 : return;
380 : }
381 0 : tevent_req_done(req);
382 : }
383 :
384 15148 : static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
385 : uint16_t *out_flags,
386 : struct timespec *out_creation_ts,
387 : struct timespec *out_last_access_ts,
388 : struct timespec *out_last_write_ts,
389 : struct timespec *out_change_ts,
390 : uint64_t *out_allocation_size,
391 : uint64_t *out_end_of_file,
392 : uint32_t *out_file_attributes)
393 : {
394 13142 : struct smbd_smb2_close_state *state =
395 15148 : tevent_req_data(req,
396 : struct smbd_smb2_close_state);
397 : NTSTATUS status;
398 :
399 15148 : if (tevent_req_is_nterror(req, &status)) {
400 0 : tevent_req_received(req);
401 0 : return status;
402 : }
403 :
404 15148 : *out_flags = state->out_flags;
405 15148 : *out_creation_ts = state->out_creation_ts;
406 15148 : *out_last_access_ts = state->out_last_access_ts;
407 15148 : *out_last_write_ts = state->out_last_write_ts;
408 15148 : *out_change_ts = state->out_change_ts;
409 15148 : *out_allocation_size = state->out_allocation_size;
410 15148 : *out_end_of_file = state->out_end_of_file;
411 15148 : *out_file_attributes = state->out_file_attributes;
412 :
413 15148 : tevent_req_received(req);
414 15148 : return NT_STATUS_OK;
415 : }
|