097786178840526b95b7ce09f5868fbbb90c0dca
[amitay/samba.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 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_SMB2
30
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);
45
46 static void smbd_smb2_request_close_done(struct tevent_req *subreq);
47
48 NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req)
49 {
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;
57
58         status = smbd_smb2_request_verify_sizes(req, 0x18);
59         if (!NT_STATUS_IS_OK(status)) {
60                 return smbd_smb2_request_error(req, status);
61         }
62         inbody = SMBD_SMB2_IN_BODY_PTR(req);
63
64         in_flags                = SVAL(inbody, 0x02);
65         in_file_id_persistent   = BVAL(inbody, 0x08);
66         in_file_id_volatile     = BVAL(inbody, 0x10);
67
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);
71         }
72
73         subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx,
74                                       req, in_fsp, in_flags);
75         if (subreq == NULL) {
76                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
77         }
78         tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
79
80         return smbd_smb2_request_pending_queue(req, subreq, 500);
81 }
82
83 static void smbd_smb2_request_close_done(struct tevent_req *subreq)
84 {
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;
100
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;
117                 }
118                 return;
119         }
120
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;
128                 }
129                 return;
130         }
131
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_full_timespec(conn->ts_res,
136                 (char *)outbody.data + 0x08, &out_creation_ts);
137         put_long_date_full_timespec(conn->ts_res,
138                 (char *)outbody.data + 0x10, &out_last_access_ts);
139         put_long_date_full_timespec(conn->ts_res,
140                 (char *)outbody.data + 0x18, &out_last_write_ts);
141         put_long_date_full_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);
146
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;
152         }
153 }
154
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)
166 {
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);
172         }
173         if (ret != 0) {
174                 return;
175         }
176
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);
183
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);
189         }
190         if (!(*out_file_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
191                 *out_end_of_file = get_file_size_stat(&smb_fname->st);
192         }
193
194         *out_allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
195 }
196
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)
208 {
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;
218
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);
223
224         *out_flags = 0;
225         *out_allocation_size = 0;
226         *out_end_of_file = 0;
227         *out_file_attributes = 0;
228
229         DEBUG(10,("smbd_smb2_close: %s - %s\n",
230                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
231
232         smbreq = smbd_smb2_fake_smb_request(req);
233         if (smbreq == NULL) {
234                 return NT_STATUS_NO_MEMORY;
235         }
236
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;
241         }
242
243         if ((in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) &&
244             (fsp->initial_delete_on_close || fsp->delete_on_close)) {
245                 /*
246                  * We might be deleting the file. Ensure we
247                  * return valid data from before the file got
248                  * removed.
249                  */
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);
261         }
262
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                          smb_fname_str_dbg(smb_fname), nt_errstr(status)));
267                 return status;
268         }
269
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);
282         }
283
284         *out_flags = flags;
285         *out_allocation_size = allocation_size;
286         *out_end_of_file = file_size;
287         *out_file_attributes = dos_attrs;
288
289         return NT_STATUS_OK;
290 }
291
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;
304         struct tevent_queue *wait_queue;
305 };
306
307 static void smbd_smb2_close_wait_done(struct tevent_req *subreq);
308
309 static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
310                                                struct tevent_context *ev,
311                                                struct smbd_smb2_request *smb2req,
312                                                struct files_struct *in_fsp,
313                                                uint16_t in_flags)
314 {
315         struct tevent_req *req;
316         struct smbd_smb2_close_state *state;
317         unsigned i;
318         NTSTATUS status;
319
320         req = tevent_req_create(mem_ctx, &state,
321                                 struct smbd_smb2_close_state);
322         if (req == NULL) {
323                 return NULL;
324         }
325         state->smb2req = smb2req;
326         state->in_fsp = in_fsp;
327         state->in_flags = in_flags;
328
329         in_fsp->closing = true;
330
331         i = 0;
332         while (i < in_fsp->num_aio_requests) {
333                 bool ok = tevent_req_cancel(in_fsp->aio_requests[i]);
334                 if (ok) {
335                         continue;
336                 }
337                 i += 1;
338         }
339
340         if (in_fsp->num_aio_requests != 0) {
341                 struct tevent_req *subreq;
342
343                 state->wait_queue = tevent_queue_create(state,
344                                         "smbd_smb2_close_send_wait_queue");
345                 if (tevent_req_nomem(state->wait_queue, req)) {
346                         return tevent_req_post(req, ev);
347                 }
348                 /*
349                  * Now wait until all aio requests on this fsp are
350                  * finished.
351                  *
352                  * We don't set a callback, as we just want to block the
353                  * wait queue and the talloc_free() of fsp->aio_request
354                  * will remove the item from the wait queue.
355                  */
356                 subreq = tevent_queue_wait_send(in_fsp->aio_requests,
357                                         smb2req->sconn->ev_ctx,
358                                         state->wait_queue);
359                 if (tevent_req_nomem(subreq, req)) {
360                         return tevent_req_post(req, ev);
361                 }
362
363                 /*
364                  * Now we add our own waiter to the end of the queue,
365                  * this way we get notified when all pending requests are
366                  * finished.
367                  */
368                 subreq = tevent_queue_wait_send(state,
369                                         smb2req->sconn->ev_ctx,
370                                         state->wait_queue);
371                 if (tevent_req_nomem(subreq, req)) {
372                         return tevent_req_post(req, ev);
373                 }
374
375                 tevent_req_set_callback(subreq, smbd_smb2_close_wait_done, req);
376                 return req;
377         }
378
379         status = smbd_smb2_close(smb2req,
380                                  state->in_fsp,
381                                  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         if (tevent_req_nterror(req, status)) {
391                 return tevent_req_post(req, ev);
392         }
393
394         tevent_req_done(req);
395         return tevent_req_post(req, ev);
396 }
397
398 static void smbd_smb2_close_wait_done(struct tevent_req *subreq)
399 {
400         struct tevent_req *req = tevent_req_callback_data(
401                 subreq, struct tevent_req);
402         struct smbd_smb2_close_state *state = tevent_req_data(
403                 req, struct smbd_smb2_close_state);
404         NTSTATUS status;
405
406         tevent_queue_wait_recv(subreq);
407         TALLOC_FREE(subreq);
408
409         status = smbd_smb2_close(state->smb2req,
410                                  state->in_fsp,
411                                  state->in_flags,
412                                  &state->out_flags,
413                                  &state->out_creation_ts,
414                                  &state->out_last_access_ts,
415                                  &state->out_last_write_ts,
416                                  &state->out_change_ts,
417                                  &state->out_allocation_size,
418                                  &state->out_end_of_file,
419                                  &state->out_file_attributes);
420         if (tevent_req_nterror(req, status)) {
421                 return;
422         }
423         tevent_req_done(req);
424 }
425
426 static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
427                                      uint16_t *out_flags,
428                                      struct timespec *out_creation_ts,
429                                      struct timespec *out_last_access_ts,
430                                      struct timespec *out_last_write_ts,
431                                      struct timespec *out_change_ts,
432                                      uint64_t *out_allocation_size,
433                                      uint64_t *out_end_of_file,
434                                      uint32_t *out_file_attributes)
435 {
436         struct smbd_smb2_close_state *state =
437                 tevent_req_data(req,
438                 struct smbd_smb2_close_state);
439         NTSTATUS status;
440
441         if (tevent_req_is_nterror(req, &status)) {
442                 tevent_req_received(req);
443                 return status;
444         }
445
446         *out_flags = state->out_flags;
447         *out_creation_ts = state->out_creation_ts;
448         *out_last_access_ts = state->out_last_access_ts;
449         *out_last_write_ts = state->out_last_write_ts;
450         *out_change_ts = state->out_change_ts;
451         *out_allocation_size = state->out_allocation_size;
452         *out_end_of_file = state->out_end_of_file;
453         *out_file_attributes = state->out_file_attributes;
454
455         tevent_req_received(req);
456         return NT_STATUS_OK;
457 }