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