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 16381 : 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 16381 : status = smbd_smb2_request_verify_sizes(req, 0x18);
58 16381 : if (!NT_STATUS_IS_OK(status)) {
59 0 : return smbd_smb2_request_error(req, status);
60 : }
61 16381 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
62 :
63 16381 : in_flags = SVAL(inbody, 0x02);
64 16381 : in_file_id_persistent = BVAL(inbody, 0x08);
65 16381 : in_file_id_volatile = BVAL(inbody, 0x10);
66 :
67 16381 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
68 16381 : if (in_fsp == NULL) {
69 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
70 : }
71 :
72 16381 : subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx,
73 : req, in_fsp, in_flags);
74 16381 : if (subreq == NULL) {
75 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
76 : }
77 16381 : tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
78 :
79 16381 : return smbd_smb2_request_pending_queue(req, subreq, 500);
80 : }
81 :
82 16381 : static void smbd_smb2_request_close_done(struct tevent_req *subreq)
83 : {
84 : struct smbd_smb2_request *req =
85 16381 : tevent_req_callback_data(subreq,
86 : struct smbd_smb2_request);
87 : DATA_BLOB outbody;
88 16381 : uint16_t out_flags = 0;
89 16381 : connection_struct *conn = req->tcon->compat;
90 16381 : struct timespec out_creation_ts = { 0, };
91 16381 : struct timespec out_last_access_ts = { 0, };
92 16381 : struct timespec out_last_write_ts = { 0, };
93 16381 : struct timespec out_change_ts = { 0, };
94 16381 : uint64_t out_allocation_size = 0;
95 16381 : uint64_t out_end_of_file = 0;
96 16381 : uint32_t out_file_attributes = 0;
97 : NTSTATUS status;
98 : NTSTATUS error;
99 :
100 16381 : 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 16381 : TALLOC_FREE(subreq);
110 16381 : 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 16381 : outbody = smbd_smb2_generate_outbody(req, 0x3C);
121 16381 : 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 16381 : SSVAL(outbody.data, 0x00, 0x3C); /* struct size */
132 16381 : SSVAL(outbody.data, 0x02, out_flags);
133 16381 : SIVAL(outbody.data, 0x04, 0); /* reserved */
134 16381 : put_long_date_full_timespec(conn->ts_res,
135 16381 : (char *)outbody.data + 0x08, &out_creation_ts);
136 16381 : put_long_date_full_timespec(conn->ts_res,
137 16381 : (char *)outbody.data + 0x10, &out_last_access_ts);
138 16381 : put_long_date_full_timespec(conn->ts_res,
139 16381 : (char *)outbody.data + 0x18, &out_last_write_ts);
140 16381 : put_long_date_full_timespec(conn->ts_res,
141 16381 : (char *)outbody.data + 0x20, &out_change_ts);
142 16381 : SBVAL(outbody.data, 0x28, out_allocation_size);
143 16381 : SBVAL(outbody.data, 0x30, out_end_of_file);
144 16381 : SIVAL(outbody.data, 0x38, out_file_attributes);
145 :
146 16381 : error = smbd_smb2_request_done(req, outbody, NULL);
147 16381 : if (!NT_STATUS_IS_OK(error)) {
148 2428 : 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 16381 : 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 16381 : connection_struct *conn = req->tcon->compat;
198 16381 : struct files_struct *fsp = *_fsp;
199 16381 : struct smb_filename *smb_fname = NULL;
200 :
201 16381 : *out_creation_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
202 16381 : *out_last_access_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
203 16381 : *out_last_write_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
204 16381 : *out_change_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
205 :
206 16381 : *out_flags = 0;
207 16381 : *out_allocation_size = 0;
208 16381 : *out_end_of_file = 0;
209 16381 : *out_file_attributes = 0;
210 :
211 16381 : DEBUG(10,("smbd_smb2_close: %s - %s\n",
212 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
213 :
214 16381 : smbreq = smbd_smb2_fake_smb_request(req);
215 16381 : if (smbreq == NULL) {
216 0 : return NT_STATUS_NO_MEMORY;
217 : }
218 :
219 16381 : 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 16381 : status = close_file_smb(smbreq, fsp, NORMAL_CLOSE);
225 16381 : 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 16381 : 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 16381 : file_free(smbreq, fsp);
246 16381 : *_fsp = fsp = NULL;
247 16381 : 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 16381 : 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 16381 : const char *fsp_name_str = NULL;
276 16381 : const char *fsp_fnum_str = NULL;
277 : unsigned i;
278 : NTSTATUS status;
279 :
280 16381 : if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
281 0 : fsp_name_str = fsp_str_dbg(in_fsp);
282 0 : fsp_fnum_str = fsp_fnum_dbg(in_fsp);
283 : }
284 :
285 16381 : DBG_DEBUG("%s - %s\n", fsp_name_str, fsp_fnum_str);
286 :
287 16381 : req = tevent_req_create(mem_ctx, &state,
288 : struct smbd_smb2_close_state);
289 16381 : if (req == NULL) {
290 0 : return NULL;
291 : }
292 16381 : state->smb2req = smb2req;
293 16381 : state->in_fsp = in_fsp;
294 16381 : state->in_flags = in_flags;
295 :
296 16381 : in_fsp->fsp_flags.closing = true;
297 :
298 16381 : i = 0;
299 16381 : while (i < in_fsp->num_aio_requests) {
300 0 : bool ok = tevent_req_cancel(in_fsp->aio_requests[i]);
301 0 : if (ok) {
302 0 : continue;
303 : }
304 0 : i += 1;
305 : }
306 :
307 16381 : if (in_fsp->num_aio_requests != 0) {
308 : struct tevent_req *subreq;
309 :
310 0 : state->wait_queue = tevent_queue_create(state,
311 : "smbd_smb2_close_send_wait_queue");
312 0 : if (tevent_req_nomem(state->wait_queue, req)) {
313 0 : return tevent_req_post(req, ev);
314 : }
315 : /*
316 : * Now wait until all aio requests on this fsp are
317 : * finished.
318 : *
319 : * We don't set a callback, as we just want to block the
320 : * wait queue and the talloc_free() of fsp->aio_request
321 : * will remove the item from the wait queue.
322 : */
323 0 : subreq = tevent_queue_wait_send(in_fsp->aio_requests,
324 0 : smb2req->sconn->ev_ctx,
325 0 : state->wait_queue);
326 0 : if (tevent_req_nomem(subreq, req)) {
327 0 : return tevent_req_post(req, ev);
328 : }
329 :
330 : /*
331 : * Now we add our own waiter to the end of the queue,
332 : * this way we get notified when all pending requests are
333 : * finished.
334 : */
335 0 : subreq = tevent_queue_wait_send(state,
336 0 : smb2req->sconn->ev_ctx,
337 0 : state->wait_queue);
338 0 : if (tevent_req_nomem(subreq, req)) {
339 0 : return tevent_req_post(req, ev);
340 : }
341 :
342 0 : tevent_req_set_callback(subreq, smbd_smb2_close_wait_done, req);
343 0 : return req;
344 : }
345 :
346 16381 : status = smbd_smb2_close(smb2req,
347 16381 : &state->in_fsp,
348 16381 : state->in_flags,
349 16381 : &state->out_flags,
350 16381 : &state->out_creation_ts,
351 16381 : &state->out_last_access_ts,
352 16381 : &state->out_last_write_ts,
353 16381 : &state->out_change_ts,
354 16381 : &state->out_allocation_size,
355 16381 : &state->out_end_of_file,
356 16381 : &state->out_file_attributes);
357 16381 : if (tevent_req_nterror(req, status)) {
358 0 : DBG_INFO("%s - %s: close file failed: %s\n",
359 : fsp_name_str, fsp_fnum_str,
360 : nt_errstr(status));
361 0 : return tevent_req_post(req, ev);
362 : }
363 :
364 16381 : tevent_req_done(req);
365 16381 : return tevent_req_post(req, ev);
366 : }
367 :
368 0 : static void smbd_smb2_close_wait_done(struct tevent_req *subreq)
369 : {
370 0 : struct tevent_req *req = tevent_req_callback_data(
371 : subreq, struct tevent_req);
372 0 : struct smbd_smb2_close_state *state = tevent_req_data(
373 : req, struct smbd_smb2_close_state);
374 : NTSTATUS status;
375 :
376 0 : tevent_queue_wait_recv(subreq);
377 0 : TALLOC_FREE(subreq);
378 :
379 0 : status = smbd_smb2_close(state->smb2req,
380 : &state->in_fsp,
381 0 : state->in_flags,
382 : &state->out_flags,
383 : &state->out_creation_ts,
384 : &state->out_last_access_ts,
385 : &state->out_last_write_ts,
386 : &state->out_change_ts,
387 : &state->out_allocation_size,
388 : &state->out_end_of_file,
389 : &state->out_file_attributes);
390 0 : if (tevent_req_nterror(req, status)) {
391 0 : return;
392 : }
393 0 : tevent_req_done(req);
394 : }
395 :
396 16381 : static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
397 : uint16_t *out_flags,
398 : struct timespec *out_creation_ts,
399 : struct timespec *out_last_access_ts,
400 : struct timespec *out_last_write_ts,
401 : struct timespec *out_change_ts,
402 : uint64_t *out_allocation_size,
403 : uint64_t *out_end_of_file,
404 : uint32_t *out_file_attributes)
405 : {
406 : struct smbd_smb2_close_state *state =
407 16381 : tevent_req_data(req,
408 : struct smbd_smb2_close_state);
409 : NTSTATUS status;
410 :
411 16381 : if (tevent_req_is_nterror(req, &status)) {
412 0 : tevent_req_received(req);
413 0 : return status;
414 : }
415 :
416 16381 : *out_flags = state->out_flags;
417 16381 : *out_creation_ts = state->out_creation_ts;
418 16381 : *out_last_access_ts = state->out_last_access_ts;
419 16381 : *out_last_write_ts = state->out_last_write_ts;
420 16381 : *out_change_ts = state->out_change_ts;
421 16381 : *out_allocation_size = state->out_allocation_size;
422 16381 : *out_end_of_file = state->out_end_of_file;
423 16381 : *out_file_attributes = state->out_file_attributes;
424 :
425 16381 : tevent_req_received(req);
426 16381 : return NT_STATUS_OK;
427 : }
|