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 369080 : NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req)
48 : {
49 505 : const uint8_t *inbody;
50 505 : uint16_t in_flags;
51 505 : uint64_t in_file_id_persistent;
52 505 : uint64_t in_file_id_volatile;
53 505 : struct files_struct *in_fsp;
54 505 : NTSTATUS status;
55 505 : struct tevent_req *subreq;
56 :
57 369080 : status = smbd_smb2_request_verify_sizes(req, 0x18);
58 369080 : if (!NT_STATUS_IS_OK(status)) {
59 0 : return smbd_smb2_request_error(req, status);
60 : }
61 369080 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
62 :
63 369080 : in_flags = SVAL(inbody, 0x02);
64 369080 : in_file_id_persistent = BVAL(inbody, 0x08);
65 369080 : in_file_id_volatile = BVAL(inbody, 0x10);
66 :
67 369080 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
68 369080 : if (in_fsp == NULL) {
69 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
70 : }
71 :
72 369080 : subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx,
73 : req, in_fsp, in_flags);
74 369080 : if (subreq == NULL) {
75 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
76 : }
77 369080 : tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
78 :
79 369080 : return smbd_smb2_request_pending_queue(req, subreq, 500);
80 : }
81 :
82 369080 : static void smbd_smb2_request_close_done(struct tevent_req *subreq)
83 : {
84 505 : struct smbd_smb2_request *req =
85 369080 : tevent_req_callback_data(subreq,
86 : struct smbd_smb2_request);
87 505 : DATA_BLOB outbody;
88 369080 : uint16_t out_flags = 0;
89 369080 : connection_struct *conn = req->tcon->compat;
90 369080 : struct timespec out_creation_ts = { 0, };
91 369080 : struct timespec out_last_access_ts = { 0, };
92 369080 : struct timespec out_last_write_ts = { 0, };
93 369080 : struct timespec out_change_ts = { 0, };
94 369080 : uint64_t out_allocation_size = 0;
95 369080 : uint64_t out_end_of_file = 0;
96 369080 : uint32_t out_file_attributes = 0;
97 505 : NTSTATUS status;
98 505 : NTSTATUS error;
99 :
100 369080 : 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 369080 : TALLOC_FREE(subreq);
110 369080 : if (!NT_STATUS_IS_OK(status)) {
111 4 : error = smbd_smb2_request_error(req, status);
112 4 : if (!NT_STATUS_IS_OK(error)) {
113 0 : smbd_server_connection_terminate(req->xconn,
114 : nt_errstr(error));
115 4 : return;
116 : }
117 4 : return;
118 : }
119 :
120 369076 : outbody = smbd_smb2_generate_outbody(req, 0x3C);
121 369076 : 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 369076 : SSVAL(outbody.data, 0x00, 0x3C); /* struct size */
132 369076 : SSVAL(outbody.data, 0x02, out_flags);
133 369076 : SIVAL(outbody.data, 0x04, 0); /* reserved */
134 369076 : put_long_date_full_timespec(conn->ts_res,
135 368571 : (char *)outbody.data + 0x08, &out_creation_ts);
136 369076 : put_long_date_full_timespec(conn->ts_res,
137 368571 : (char *)outbody.data + 0x10, &out_last_access_ts);
138 369076 : put_long_date_full_timespec(conn->ts_res,
139 368571 : (char *)outbody.data + 0x18, &out_last_write_ts);
140 369076 : put_long_date_full_timespec(conn->ts_res,
141 368571 : (char *)outbody.data + 0x20, &out_change_ts);
142 369076 : SBVAL(outbody.data, 0x28, out_allocation_size);
143 369076 : SBVAL(outbody.data, 0x30, out_end_of_file);
144 369076 : SIVAL(outbody.data, 0x38, out_file_attributes);
145 :
146 369076 : error = smbd_smb2_request_done(req, outbody, NULL);
147 369076 : if (!NT_STATUS_IS_OK(error)) {
148 3943 : smbd_server_connection_terminate(req->xconn,
149 : nt_errstr(error));
150 147 : return;
151 : }
152 : }
153 :
154 34 : 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 34 : *out_flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
165 34 : *out_last_write_ts = smb_fname->st.st_ex_mtime;
166 34 : *out_last_access_ts = smb_fname->st.st_ex_atime;
167 34 : *out_creation_ts = get_create_timespec(conn, NULL, smb_fname);
168 34 : *out_change_ts = get_change_timespec(conn, NULL, smb_fname);
169 :
170 34 : 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 34 : if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
177 34 : *out_end_of_file = get_file_size_stat(&smb_fname->st);
178 : }
179 :
180 34 : *out_allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
181 34 : }
182 :
183 369080 : 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 505 : NTSTATUS status;
196 505 : struct smb_request *smbreq;
197 369080 : connection_struct *conn = req->tcon->compat;
198 369080 : struct files_struct *fsp = *_fsp;
199 369080 : struct smb_filename *smb_fname = NULL;
200 :
201 369080 : *out_creation_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
202 369080 : *out_last_access_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
203 369080 : *out_last_write_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
204 369080 : *out_change_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
205 :
206 369080 : *out_flags = 0;
207 369080 : *out_allocation_size = 0;
208 369080 : *out_end_of_file = 0;
209 369080 : *out_file_attributes = 0;
210 :
211 369080 : DEBUG(10,("smbd_smb2_close: %s - %s\n",
212 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
213 :
214 369080 : smbreq = smbd_smb2_fake_smb_request(req, fsp);
215 369080 : if (smbreq == NULL) {
216 0 : return NT_STATUS_NO_MEMORY;
217 : }
218 :
219 369080 : if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
220 34 : *out_file_attributes = fdos_mode(fsp);
221 34 : fsp->fsp_flags.fstat_before_close = true;
222 : }
223 :
224 369080 : status = close_file_smb(smbreq, fsp, NORMAL_CLOSE);
225 369080 : if (!NT_STATUS_IS_OK(status)) {
226 4 : DEBUG(5,("smbd_smb2_close: close_file[%s]: %s\n",
227 : smb_fname_str_dbg(smb_fname), nt_errstr(status)));
228 4 : file_free(smbreq, fsp);
229 4 : *_fsp = fsp = NULL;
230 4 : return status;
231 : }
232 :
233 369076 : if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
234 34 : 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 369076 : file_free(smbreq, fsp);
246 369076 : *_fsp = fsp = NULL;
247 369076 : 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 369080 : 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 505 : struct tevent_req *req;
274 505 : struct smbd_smb2_close_state *state;
275 369080 : const char *fsp_name_str = NULL;
276 369080 : const char *fsp_fnum_str = NULL;
277 505 : unsigned i;
278 505 : NTSTATUS status;
279 :
280 369080 : 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 369080 : DBG_DEBUG("%s - %s\n", fsp_name_str, fsp_fnum_str);
286 :
287 369080 : req = tevent_req_create(mem_ctx, &state,
288 : struct smbd_smb2_close_state);
289 369080 : if (req == NULL) {
290 0 : return NULL;
291 : }
292 369080 : state->smb2req = smb2req;
293 369080 : state->in_fsp = in_fsp;
294 369080 : state->in_flags = in_flags;
295 :
296 369080 : in_fsp->fsp_flags.closing = true;
297 :
298 369080 : i = 0;
299 369098 : while (i < in_fsp->num_aio_requests) {
300 18 : bool ok = tevent_req_cancel(in_fsp->aio_requests[i]);
301 18 : if (ok) {
302 8 : continue;
303 : }
304 10 : i += 1;
305 : }
306 :
307 369080 : if (in_fsp->num_aio_requests != 0) {
308 0 : struct tevent_req *subreq;
309 :
310 10 : state->wait_queue = tevent_queue_create(state,
311 : "smbd_smb2_close_send_wait_queue");
312 10 : 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 10 : subreq = tevent_queue_wait_send(in_fsp->aio_requests,
324 10 : smb2req->sconn->ev_ctx,
325 10 : state->wait_queue);
326 10 : 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 10 : subreq = tevent_queue_wait_send(state,
336 10 : smb2req->sconn->ev_ctx,
337 10 : state->wait_queue);
338 10 : if (tevent_req_nomem(subreq, req)) {
339 0 : return tevent_req_post(req, ev);
340 : }
341 :
342 10 : tevent_req_set_callback(subreq, smbd_smb2_close_wait_done, req);
343 10 : return req;
344 : }
345 :
346 369070 : status = smbd_smb2_close(smb2req,
347 368565 : &state->in_fsp,
348 369070 : state->in_flags,
349 368565 : &state->out_flags,
350 368565 : &state->out_creation_ts,
351 368565 : &state->out_last_access_ts,
352 368565 : &state->out_last_write_ts,
353 368565 : &state->out_change_ts,
354 368565 : &state->out_allocation_size,
355 368565 : &state->out_end_of_file,
356 369070 : &state->out_file_attributes);
357 369070 : if (tevent_req_nterror(req, status)) {
358 4 : DBG_INFO("%s - %s: close file failed: %s\n",
359 : fsp_name_str, fsp_fnum_str,
360 : nt_errstr(status));
361 4 : return tevent_req_post(req, ev);
362 : }
363 :
364 369066 : tevent_req_done(req);
365 369066 : return tevent_req_post(req, ev);
366 : }
367 :
368 10 : static void smbd_smb2_close_wait_done(struct tevent_req *subreq)
369 : {
370 10 : struct tevent_req *req = tevent_req_callback_data(
371 : subreq, struct tevent_req);
372 10 : struct smbd_smb2_close_state *state = tevent_req_data(
373 : req, struct smbd_smb2_close_state);
374 0 : NTSTATUS status;
375 :
376 10 : tevent_queue_wait_recv(subreq);
377 10 : TALLOC_FREE(subreq);
378 :
379 10 : status = smbd_smb2_close(state->smb2req,
380 : &state->in_fsp,
381 10 : 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 10 : if (tevent_req_nterror(req, status)) {
391 0 : return;
392 : }
393 10 : tevent_req_done(req);
394 : }
395 :
396 369080 : 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 505 : struct smbd_smb2_close_state *state =
407 369080 : tevent_req_data(req,
408 : struct smbd_smb2_close_state);
409 505 : NTSTATUS status;
410 :
411 369080 : if (tevent_req_is_nterror(req, &status)) {
412 4 : tevent_req_received(req);
413 4 : return status;
414 : }
415 :
416 369076 : *out_flags = state->out_flags;
417 369076 : *out_creation_ts = state->out_creation_ts;
418 369076 : *out_last_access_ts = state->out_last_access_ts;
419 369076 : *out_last_write_ts = state->out_last_write_ts;
420 369076 : *out_change_ts = state->out_change_ts;
421 369076 : *out_allocation_size = state->out_allocation_size;
422 369076 : *out_end_of_file = state->out_end_of_file;
423 369076 : *out_file_attributes = state->out_file_attributes;
424 :
425 369076 : tevent_req_received(req);
426 369076 : return NT_STATUS_OK;
427 : }
|