VERSION: Bump version up to 4.9.2...
[Samba.git] / source3 / smbd / smb2_close.c
blob33863d32f5f2e82d21442900f8e85f7dc4e3697d
1 /*
2 Unix SMB/CIFS implementation.
3 Core SMB2 server
5 Copyright (C) Stefan Metzmacher 2009
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.
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.
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/>.
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 "lib/tevent_wait.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_SMB2
31 static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
32 struct tevent_context *ev,
33 struct smbd_smb2_request *smb2req,
34 struct files_struct *in_fsp,
35 uint16_t in_flags);
36 static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
37 uint16_t *out_flags,
38 struct timespec *out_creation_ts,
39 struct timespec *out_last_access_ts,
40 struct timespec *out_last_write_ts,
41 struct timespec *out_change_ts,
42 uint64_t *out_allocation_size,
43 uint64_t *out_end_of_file,
44 uint32_t *out_file_attributes);
46 static void smbd_smb2_request_close_done(struct tevent_req *subreq);
48 NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req)
50 const uint8_t *inbody;
51 uint16_t in_flags;
52 uint64_t in_file_id_persistent;
53 uint64_t in_file_id_volatile;
54 struct files_struct *in_fsp;
55 NTSTATUS status;
56 struct tevent_req *subreq;
58 status = smbd_smb2_request_verify_sizes(req, 0x18);
59 if (!NT_STATUS_IS_OK(status)) {
60 return smbd_smb2_request_error(req, status);
62 inbody = SMBD_SMB2_IN_BODY_PTR(req);
64 in_flags = SVAL(inbody, 0x02);
65 in_file_id_persistent = BVAL(inbody, 0x08);
66 in_file_id_volatile = BVAL(inbody, 0x10);
68 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
69 if (in_fsp == NULL) {
70 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
73 subreq = smbd_smb2_close_send(req, req->ev_ctx,
74 req, in_fsp, in_flags);
75 if (subreq == NULL) {
76 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
78 tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
80 return smbd_smb2_request_pending_queue(req, subreq, 500);
83 static void smbd_smb2_request_close_done(struct tevent_req *subreq)
85 struct smbd_smb2_request *req =
86 tevent_req_callback_data(subreq,
87 struct smbd_smb2_request);
88 DATA_BLOB outbody;
89 uint16_t out_flags = 0;
90 connection_struct *conn = req->tcon->compat;
91 struct timespec out_creation_ts = { 0, };
92 struct timespec out_last_access_ts = { 0, };
93 struct timespec out_last_write_ts = { 0, };
94 struct timespec out_change_ts = { 0, };
95 uint64_t out_allocation_size = 0;
96 uint64_t out_end_of_file = 0;
97 uint32_t out_file_attributes = 0;
98 NTSTATUS status;
99 NTSTATUS error;
101 status = smbd_smb2_close_recv(subreq,
102 &out_flags,
103 &out_creation_ts,
104 &out_last_access_ts,
105 &out_last_write_ts,
106 &out_change_ts,
107 &out_allocation_size,
108 &out_end_of_file,
109 &out_file_attributes);
110 TALLOC_FREE(subreq);
111 if (!NT_STATUS_IS_OK(status)) {
112 error = smbd_smb2_request_error(req, status);
113 if (!NT_STATUS_IS_OK(error)) {
114 smbd_server_connection_terminate(req->xconn,
115 nt_errstr(error));
116 return;
118 return;
121 outbody = smbd_smb2_generate_outbody(req, 0x3C);
122 if (outbody.data == NULL) {
123 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
124 if (!NT_STATUS_IS_OK(error)) {
125 smbd_server_connection_terminate(req->xconn,
126 nt_errstr(error));
127 return;
129 return;
132 SSVAL(outbody.data, 0x00, 0x3C); /* struct size */
133 SSVAL(outbody.data, 0x02, out_flags);
134 SIVAL(outbody.data, 0x04, 0); /* reserved */
135 put_long_date_timespec(conn->ts_res,
136 (char *)outbody.data + 0x08, out_creation_ts);
137 put_long_date_timespec(conn->ts_res,
138 (char *)outbody.data + 0x10, out_last_access_ts);
139 put_long_date_timespec(conn->ts_res,
140 (char *)outbody.data + 0x18, out_last_write_ts);
141 put_long_date_timespec(conn->ts_res,
142 (char *)outbody.data + 0x20, out_change_ts);
143 SBVAL(outbody.data, 0x28, out_allocation_size);
144 SBVAL(outbody.data, 0x30, out_end_of_file);
145 SIVAL(outbody.data, 0x38, out_file_attributes);
147 error = smbd_smb2_request_done(req, outbody, NULL);
148 if (!NT_STATUS_IS_OK(error)) {
149 smbd_server_connection_terminate(req->xconn,
150 nt_errstr(error));
151 return;
155 static void setup_close_full_information(connection_struct *conn,
156 struct smb_filename *smb_fname,
157 bool posix_open,
158 struct timespec *out_creation_ts,
159 struct timespec *out_last_access_ts,
160 struct timespec *out_last_write_ts,
161 struct timespec *out_change_ts,
162 uint16_t *out_flags,
163 uint64_t *out_allocation_size,
164 uint64_t *out_end_of_file,
165 uint32_t *out_file_attributes)
167 int ret;
168 if (posix_open) {
169 ret = SMB_VFS_LSTAT(conn, smb_fname);
170 } else {
171 ret = SMB_VFS_STAT(conn, smb_fname);
173 if (ret != 0) {
174 return;
177 *out_flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
178 *out_file_attributes = dos_mode(conn, smb_fname);
179 *out_last_write_ts = smb_fname->st.st_ex_mtime;
180 *out_last_access_ts = smb_fname->st.st_ex_atime;
181 *out_creation_ts = get_create_timespec(conn, NULL, smb_fname);
182 *out_change_ts = get_change_timespec(conn, NULL, smb_fname);
184 if (lp_dos_filetime_resolution(SNUM(conn))) {
185 dos_filetime_timespec(out_creation_ts);
186 dos_filetime_timespec(out_last_write_ts);
187 dos_filetime_timespec(out_last_access_ts);
188 dos_filetime_timespec(out_change_ts);
190 if (!(*out_file_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
191 *out_end_of_file = get_file_size_stat(&smb_fname->st);
194 *out_allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
197 static NTSTATUS smbd_smb2_close(struct smbd_smb2_request *req,
198 struct files_struct *fsp,
199 uint16_t in_flags,
200 uint16_t *out_flags,
201 struct timespec *out_creation_ts,
202 struct timespec *out_last_access_ts,
203 struct timespec *out_last_write_ts,
204 struct timespec *out_change_ts,
205 uint64_t *out_allocation_size,
206 uint64_t *out_end_of_file,
207 uint32_t *out_file_attributes)
209 NTSTATUS status;
210 struct smb_request *smbreq;
211 connection_struct *conn = req->tcon->compat;
212 struct smb_filename *smb_fname = NULL;
213 uint64_t allocation_size = 0;
214 uint64_t file_size = 0;
215 uint32_t dos_attrs = 0;
216 uint16_t flags = 0;
217 bool posix_open = false;
219 ZERO_STRUCTP(out_creation_ts);
220 ZERO_STRUCTP(out_last_access_ts);
221 ZERO_STRUCTP(out_last_write_ts);
222 ZERO_STRUCTP(out_change_ts);
224 *out_flags = 0;
225 *out_allocation_size = 0;
226 *out_end_of_file = 0;
227 *out_file_attributes = 0;
229 DEBUG(10,("smbd_smb2_close: %s - %s\n",
230 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
232 smbreq = smbd_smb2_fake_smb_request(req);
233 if (smbreq == NULL) {
234 return NT_STATUS_NO_MEMORY;
237 posix_open = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN);
238 smb_fname = cp_smb_filename(talloc_tos(), fsp->fsp_name);
239 if (smb_fname == NULL) {
240 return NT_STATUS_NO_MEMORY;
243 if ((in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) &&
244 (fsp->initial_delete_on_close || fsp->delete_on_close)) {
246 * We might be deleting the file. Ensure we
247 * return valid data from before the file got
248 * removed.
250 setup_close_full_information(conn,
251 smb_fname,
252 posix_open,
253 out_creation_ts,
254 out_last_access_ts,
255 out_last_write_ts,
256 out_change_ts,
257 &flags,
258 &allocation_size,
259 &file_size,
260 &dos_attrs);
263 status = close_file(smbreq, fsp, NORMAL_CLOSE);
264 if (!NT_STATUS_IS_OK(status)) {
265 DEBUG(5,("smbd_smb2_close: close_file[%s]: %s\n",
266 fsp_str_dbg(fsp), nt_errstr(status)));
267 return status;
270 if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
271 setup_close_full_information(conn,
272 smb_fname,
273 posix_open,
274 out_creation_ts,
275 out_last_access_ts,
276 out_last_write_ts,
277 out_change_ts,
278 &flags,
279 &allocation_size,
280 &file_size,
281 &dos_attrs);
284 *out_flags = flags;
285 *out_allocation_size = allocation_size;
286 *out_end_of_file = file_size;
287 *out_file_attributes = dos_attrs;
289 return NT_STATUS_OK;
292 struct smbd_smb2_close_state {
293 struct smbd_smb2_request *smb2req;
294 struct files_struct *in_fsp;
295 uint16_t in_flags;
296 uint16_t out_flags;
297 struct timespec out_creation_ts;
298 struct timespec out_last_access_ts;
299 struct timespec out_last_write_ts;
300 struct timespec out_change_ts;
301 uint64_t out_allocation_size;
302 uint64_t out_end_of_file;
303 uint32_t out_file_attributes;
306 static void smbd_smb2_close_do(struct tevent_req *subreq);
308 static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
309 struct tevent_context *ev,
310 struct smbd_smb2_request *smb2req,
311 struct files_struct *in_fsp,
312 uint16_t in_flags)
314 struct tevent_req *req;
315 struct smbd_smb2_close_state *state;
316 NTSTATUS status;
318 req = tevent_req_create(mem_ctx, &state,
319 struct smbd_smb2_close_state);
320 if (req == NULL) {
321 return NULL;
323 state->smb2req = smb2req;
324 state->in_fsp = in_fsp;
325 state->in_flags = in_flags;
327 if (in_fsp->num_aio_requests != 0) {
328 in_fsp->deferred_close = tevent_wait_send(in_fsp, ev);
329 if (tevent_req_nomem(in_fsp->deferred_close, req)) {
330 return tevent_req_post(req, ev);
332 tevent_req_set_callback(in_fsp->deferred_close,
333 smbd_smb2_close_do, req);
334 return req;
337 status = smbd_smb2_close(smb2req,
338 state->in_fsp,
339 state->in_flags,
340 &state->out_flags,
341 &state->out_creation_ts,
342 &state->out_last_access_ts,
343 &state->out_last_write_ts,
344 &state->out_change_ts,
345 &state->out_allocation_size,
346 &state->out_end_of_file,
347 &state->out_file_attributes);
348 if (tevent_req_nterror(req, status)) {
349 return tevent_req_post(req, ev);
352 tevent_req_done(req);
353 return tevent_req_post(req, ev);
356 static void smbd_smb2_close_do(struct tevent_req *subreq)
358 struct tevent_req *req = tevent_req_callback_data(
359 subreq, struct tevent_req);
360 struct smbd_smb2_close_state *state = tevent_req_data(
361 req, struct smbd_smb2_close_state);
362 NTSTATUS status;
363 int ret;
365 ret = tevent_wait_recv(subreq);
366 TALLOC_FREE(subreq);
367 if (ret != 0) {
368 DEBUG(10, ("tevent_wait_recv returned %s\n",
369 strerror(ret)));
371 * Continue anyway, this should never happen
375 status = smbd_smb2_close(state->smb2req,
376 state->in_fsp,
377 state->in_flags,
378 &state->out_flags,
379 &state->out_creation_ts,
380 &state->out_last_access_ts,
381 &state->out_last_write_ts,
382 &state->out_change_ts,
383 &state->out_allocation_size,
384 &state->out_end_of_file,
385 &state->out_file_attributes);
386 if (tevent_req_nterror(req, status)) {
387 return;
389 tevent_req_done(req);
392 static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
393 uint16_t *out_flags,
394 struct timespec *out_creation_ts,
395 struct timespec *out_last_access_ts,
396 struct timespec *out_last_write_ts,
397 struct timespec *out_change_ts,
398 uint64_t *out_allocation_size,
399 uint64_t *out_end_of_file,
400 uint32_t *out_file_attributes)
402 struct smbd_smb2_close_state *state =
403 tevent_req_data(req,
404 struct smbd_smb2_close_state);
405 NTSTATUS status;
407 if (tevent_req_is_nterror(req, &status)) {
408 tevent_req_received(req);
409 return status;
412 *out_flags = state->out_flags;
413 *out_creation_ts = state->out_creation_ts;
414 *out_last_access_ts = state->out_last_access_ts;
415 *out_last_write_ts = state->out_last_write_ts;
416 *out_change_ts = state->out_change_ts;
417 *out_allocation_size = state->out_allocation_size;
418 *out_end_of_file = state->out_end_of_file;
419 *out_file_attributes = state->out_file_attributes;
421 tevent_req_received(req);
422 return NT_STATUS_OK;