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 367290 : NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req)
48 : {
49 504 : const uint8_t *inbody;
50 504 : uint16_t in_flags;
51 504 : uint64_t in_file_id_persistent;
52 504 : uint64_t in_file_id_volatile;
53 504 : struct files_struct *in_fsp;
54 504 : NTSTATUS status;
55 504 : struct tevent_req *subreq;
56 :
57 367290 : status = smbd_smb2_request_verify_sizes(req, 0x18);
58 367290 : if (!NT_STATUS_IS_OK(status)) {
59 0 : return smbd_smb2_request_error(req, status);
60 : }
61 367290 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
62 :
63 367290 : in_flags = SVAL(inbody, 0x02);
64 367290 : in_file_id_persistent = BVAL(inbody, 0x08);
65 367290 : in_file_id_volatile = BVAL(inbody, 0x10);
66 :
67 367290 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
68 367290 : if (in_fsp == NULL) {
69 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
70 : }
71 :
72 367290 : subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx,
73 : req, in_fsp, in_flags);
74 367290 : if (subreq == NULL) {
75 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
76 : }
77 367290 : tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
78 :
79 367290 : return smbd_smb2_request_pending_queue(req, subreq, 500);
80 : }
81 :
82 367290 : static void smbd_smb2_request_close_done(struct tevent_req *subreq)
83 : {
84 504 : struct smbd_smb2_request *req =
85 367290 : tevent_req_callback_data(subreq,
86 : struct smbd_smb2_request);
87 504 : DATA_BLOB outbody;
88 367290 : uint16_t out_flags = 0;
89 367290 : connection_struct *conn = req->tcon->compat;
90 367290 : struct timespec out_creation_ts = { 0, };
91 367290 : struct timespec out_last_access_ts = { 0, };
92 367290 : struct timespec out_last_write_ts = { 0, };
93 367290 : struct timespec out_change_ts = { 0, };
94 367290 : uint64_t out_allocation_size = 0;
95 367290 : uint64_t out_end_of_file = 0;
96 367290 : uint32_t out_file_attributes = 0;
97 504 : NTSTATUS status;
98 504 : NTSTATUS error;
99 :
100 367290 : 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 367290 : TALLOC_FREE(subreq);
110 367290 : 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 367286 : outbody = smbd_smb2_generate_outbody(req, 0x3C);
121 367286 : 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 367286 : SSVAL(outbody.data, 0x00, 0x3C); /* struct size */
132 367286 : SSVAL(outbody.data, 0x02, out_flags);
133 367286 : SIVAL(outbody.data, 0x04, 0); /* reserved */
134 367286 : put_long_date_full_timespec(conn->ts_res,
135 366782 : (char *)outbody.data + 0x08, &out_creation_ts);
136 367286 : put_long_date_full_timespec(conn->ts_res,
137 366782 : (char *)outbody.data + 0x10, &out_last_access_ts);
138 367286 : put_long_date_full_timespec(conn->ts_res,
139 366782 : (char *)outbody.data + 0x18, &out_last_write_ts);
140 367286 : put_long_date_full_timespec(conn->ts_res,
141 366782 : (char *)outbody.data + 0x20, &out_change_ts);
142 367286 : SBVAL(outbody.data, 0x28, out_allocation_size);
143 367286 : SBVAL(outbody.data, 0x30, out_end_of_file);
144 367286 : SIVAL(outbody.data, 0x38, out_file_attributes);
145 :
146 367286 : error = smbd_smb2_request_done(req, outbody, NULL);
147 367286 : if (!NT_STATUS_IS_OK(error)) {
148 3971 : smbd_server_connection_terminate(req->xconn,
149 : nt_errstr(error));
150 151 : 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 367290 : 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 504 : NTSTATUS status;
196 504 : struct smb_request *smbreq;
197 367290 : connection_struct *conn = req->tcon->compat;
198 367290 : struct files_struct *fsp = *_fsp;
199 367290 : struct smb_filename *smb_fname = NULL;
200 :
201 367290 : *out_creation_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
202 367290 : *out_last_access_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
203 367290 : *out_last_write_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
204 367290 : *out_change_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
205 :
206 367290 : *out_flags = 0;
207 367290 : *out_allocation_size = 0;
208 367290 : *out_end_of_file = 0;
209 367290 : *out_file_attributes = 0;
210 :
211 367290 : DEBUG(10,("smbd_smb2_close: %s - %s\n",
212 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
213 :
214 367290 : smbreq = smbd_smb2_fake_smb_request(req, fsp);
215 367290 : if (smbreq == NULL) {
216 0 : return NT_STATUS_NO_MEMORY;
217 : }
218 :
219 367290 : 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 367290 : status = close_file_smb(smbreq, fsp, NORMAL_CLOSE);
225 367290 : 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 367286 : 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 367286 : file_free(smbreq, fsp);
246 367286 : *_fsp = fsp = NULL;
247 367286 : 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 367290 : 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 504 : struct tevent_req *req;
274 504 : struct smbd_smb2_close_state *state;
275 367290 : const char *fsp_name_str = NULL;
276 367290 : const char *fsp_fnum_str = NULL;
277 504 : unsigned i;
278 504 : NTSTATUS status;
279 :
280 367290 : 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 367290 : DBG_DEBUG("%s - %s\n", fsp_name_str, fsp_fnum_str);
286 :
287 367290 : req = tevent_req_create(mem_ctx, &state,
288 : struct smbd_smb2_close_state);
289 367290 : if (req == NULL) {
290 0 : return NULL;
291 : }
292 367290 : state->smb2req = smb2req;
293 367290 : state->in_fsp = in_fsp;
294 367290 : state->in_flags = in_flags;
295 :
296 367290 : in_fsp->fsp_flags.closing = true;
297 :
298 367290 : i = 0;
299 367308 : 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 367290 : 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 367280 : status = smbd_smb2_close(smb2req,
347 366776 : &state->in_fsp,
348 367280 : state->in_flags,
349 366776 : &state->out_flags,
350 366776 : &state->out_creation_ts,
351 366776 : &state->out_last_access_ts,
352 366776 : &state->out_last_write_ts,
353 366776 : &state->out_change_ts,
354 366776 : &state->out_allocation_size,
355 366776 : &state->out_end_of_file,
356 367280 : &state->out_file_attributes);
357 367280 : 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 367276 : tevent_req_done(req);
365 367276 : 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 367290 : 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 504 : struct smbd_smb2_close_state *state =
407 367290 : tevent_req_data(req,
408 : struct smbd_smb2_close_state);
409 504 : NTSTATUS status;
410 :
411 367290 : if (tevent_req_is_nterror(req, &status)) {
412 4 : tevent_req_received(req);
413 4 : return status;
414 : }
415 :
416 367286 : *out_flags = state->out_flags;
417 367286 : *out_creation_ts = state->out_creation_ts;
418 367286 : *out_last_access_ts = state->out_last_access_ts;
419 367286 : *out_last_write_ts = state->out_last_write_ts;
420 367286 : *out_change_ts = state->out_change_ts;
421 367286 : *out_allocation_size = state->out_allocation_size;
422 367286 : *out_end_of_file = state->out_end_of_file;
423 367286 : *out_file_attributes = state->out_file_attributes;
424 :
425 367286 : tevent_req_received(req);
426 367286 : return NT_STATUS_OK;
427 : }
|