3712ffe21d21334d6c9bd876a4b20b52bd2bdc40
[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 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                                      NTTIME *out_creation_time,
36                                      NTTIME *out_last_access_time,
37                                      NTTIME *out_last_write_time,
38                                      NTTIME *out_change_time,
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         NTTIME out_creation_time = 0;
88         NTTIME out_last_access_time = 0;
89         NTTIME out_last_write_time = 0;
90         NTTIME out_change_time = 0;
91         uint64_t out_allocation_size = 0;
92         uint64_t out_end_of_file = 0;
93         uint32_t out_file_attributes = 0;
94         NTSTATUS status;
95         NTSTATUS error;
96
97         status = smbd_smb2_close_recv(subreq,
98                                       &out_flags,
99                                       &out_creation_time,
100                                       &out_last_access_time,
101                                       &out_last_write_time,
102                                       &out_change_time,
103                                       &out_allocation_size,
104                                       &out_end_of_file,
105                                       &out_file_attributes);
106         TALLOC_FREE(subreq);
107         if (!NT_STATUS_IS_OK(status)) {
108                 error = smbd_smb2_request_error(req, status);
109                 if (!NT_STATUS_IS_OK(error)) {
110                         smbd_server_connection_terminate(req->sconn,
111                                                          nt_errstr(error));
112                         return;
113                 }
114                 return;
115         }
116
117         outbody = smbd_smb2_generate_outbody(req, 0x3C);
118         if (outbody.data == NULL) {
119                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
120                 if (!NT_STATUS_IS_OK(error)) {
121                         smbd_server_connection_terminate(req->sconn,
122                                                          nt_errstr(error));
123                         return;
124                 }
125                 return;
126         }
127
128         SSVAL(outbody.data, 0x00, 0x3C);        /* struct size */
129         SSVAL(outbody.data, 0x02, out_flags);
130         SIVAL(outbody.data, 0x04, 0);           /* reserved */
131         SBVAL(outbody.data, 0x08, out_creation_time);
132         SBVAL(outbody.data, 0x10, out_last_access_time);
133         SBVAL(outbody.data, 0x18, out_last_write_time);
134         SBVAL(outbody.data, 0x20, out_change_time);
135         SBVAL(outbody.data, 0x28, out_allocation_size);
136         SBVAL(outbody.data, 0x30, out_end_of_file);
137         SIVAL(outbody.data, 0x38, out_file_attributes);
138
139         error = smbd_smb2_request_done(req, outbody, NULL);
140         if (!NT_STATUS_IS_OK(error)) {
141                 smbd_server_connection_terminate(req->sconn,
142                                                  nt_errstr(error));
143                 return;
144         }
145 }
146
147 static NTSTATUS smbd_smb2_close(struct smbd_smb2_request *req,
148                                 struct files_struct *fsp,
149                                 uint16_t in_flags,
150                                 uint16_t *out_flags,
151                                 NTTIME *out_creation_time,
152                                 NTTIME *out_last_access_time,
153                                 NTTIME *out_last_write_time,
154                                 NTTIME *out_change_time,
155                                 uint64_t *out_allocation_size,
156                                 uint64_t *out_end_of_file,
157                                 uint32_t *out_file_attributes)
158 {
159         NTSTATUS status;
160         struct smb_request *smbreq;
161         connection_struct *conn = req->tcon->compat;
162         struct smb_filename *smb_fname = NULL;
163         struct timespec mdate_ts, adate_ts, cdate_ts, create_date_ts;
164         uint64_t allocation_size = 0;
165         uint64_t file_size = 0;
166         uint32_t dos_attrs = 0;
167         uint16_t flags = 0;
168         bool posix_open = false;
169
170         ZERO_STRUCT(create_date_ts);
171         ZERO_STRUCT(adate_ts);
172         ZERO_STRUCT(mdate_ts);
173         ZERO_STRUCT(cdate_ts);
174
175         *out_flags = 0;
176         *out_creation_time = 0;
177         *out_last_access_time = 0;
178         *out_last_write_time = 0;
179         *out_change_time = 0;
180         *out_allocation_size = 0;
181         *out_end_of_file = 0;
182         *out_file_attributes = 0;
183
184         DEBUG(10,("smbd_smb2_close: %s - %s\n",
185                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
186
187         smbreq = smbd_smb2_fake_smb_request(req);
188         if (smbreq == NULL) {
189                 return NT_STATUS_NO_MEMORY;
190         }
191
192         posix_open = fsp->posix_open;
193         smb_fname = cp_smb_filename(talloc_tos(), fsp->fsp_name);
194         if (smb_fname == NULL) {
195                 return NT_STATUS_NO_MEMORY;
196         }
197
198         status = close_file(smbreq, fsp, NORMAL_CLOSE);
199         if (!NT_STATUS_IS_OK(status)) {
200                 DEBUG(5,("smbd_smb2_close: close_file[%s]: %s\n",
201                          fsp_str_dbg(fsp), nt_errstr(status)));
202                 return status;
203         }
204
205         if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
206                 int ret;
207                 if (posix_open) {
208                         ret = SMB_VFS_LSTAT(conn, smb_fname);
209                 } else {
210                         ret = SMB_VFS_STAT(conn, smb_fname);
211                 }
212                 if (ret == 0) {
213                         flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
214                         dos_attrs = dos_mode(conn, smb_fname);
215                         mdate_ts = smb_fname->st.st_ex_mtime;
216                         adate_ts = smb_fname->st.st_ex_atime;
217                         create_date_ts = get_create_timespec(conn, NULL, smb_fname);
218                         cdate_ts = get_change_timespec(conn, NULL, smb_fname);
219
220                         if (lp_dos_filetime_resolution(SNUM(conn))) {
221                                 dos_filetime_timespec(&create_date_ts);
222                                 dos_filetime_timespec(&mdate_ts);
223                                 dos_filetime_timespec(&adate_ts);
224                                 dos_filetime_timespec(&cdate_ts);
225                         }
226                         if (!(dos_attrs & FILE_ATTRIBUTE_DIRECTORY)) {
227                                 file_size = get_file_size_stat(&smb_fname->st);
228                         }
229
230                         allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
231                 }
232         }
233
234         *out_flags = flags;
235
236         round_timespec(conn->ts_res, &create_date_ts);
237         unix_timespec_to_nt_time(out_creation_time, create_date_ts);
238
239         round_timespec(conn->ts_res, &adate_ts);
240         unix_timespec_to_nt_time(out_last_access_time, adate_ts);
241
242         round_timespec(conn->ts_res, &mdate_ts);
243         unix_timespec_to_nt_time(out_last_write_time, mdate_ts);
244
245         round_timespec(conn->ts_res, &cdate_ts);
246         unix_timespec_to_nt_time(out_change_time, cdate_ts);
247
248         *out_allocation_size = allocation_size;
249         *out_end_of_file = file_size;
250         *out_file_attributes = dos_attrs;
251
252         return NT_STATUS_OK;
253 }
254
255 struct smbd_smb2_close_state {
256         struct smbd_smb2_request *smb2req;
257         struct files_struct *in_fsp;
258         uint16_t in_flags;
259         uint16_t out_flags;
260         NTTIME out_creation_time;
261         NTTIME out_last_access_time;
262         NTTIME out_last_write_time;
263         NTTIME out_change_time;
264         uint64_t out_allocation_size;
265         uint64_t out_end_of_file;
266         uint32_t out_file_attributes;
267 };
268
269 static void smbd_smb2_close_do(struct tevent_req *subreq);
270
271 static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
272                                                struct tevent_context *ev,
273                                                struct smbd_smb2_request *smb2req,
274                                                struct files_struct *in_fsp,
275                                                uint16_t in_flags)
276 {
277         struct tevent_req *req;
278         struct smbd_smb2_close_state *state;
279         NTSTATUS status;
280
281         req = tevent_req_create(mem_ctx, &state,
282                                 struct smbd_smb2_close_state);
283         if (req == NULL) {
284                 return NULL;
285         }
286         state->smb2req = smb2req;
287         state->in_fsp = in_fsp;
288         state->in_flags = in_flags;
289
290         if (in_fsp->num_aio_requests != 0) {
291
292                 in_fsp->deferred_close = tevent_wait_send(in_fsp, ev);
293                 if (tevent_req_nomem(in_fsp->deferred_close, req)) {
294                         return tevent_req_post(req, ev);
295                 }
296                 tevent_req_set_callback(in_fsp->deferred_close,
297                                         smbd_smb2_close_do, req);
298                 return req;
299         }
300
301         status = smbd_smb2_close(smb2req,
302                                  state->in_fsp,
303                                  state->in_flags,
304                                  &state->out_flags,
305                                  &state->out_creation_time,
306                                  &state->out_last_access_time,
307                                  &state->out_last_write_time,
308                                  &state->out_change_time,
309                                  &state->out_allocation_size,
310                                  &state->out_end_of_file,
311                                  &state->out_file_attributes);
312         if (tevent_req_nterror(req, status)) {
313                 return tevent_req_post(req, ev);
314         }
315
316         tevent_req_done(req);
317         return tevent_req_post(req, ev);
318 }
319
320 static void smbd_smb2_close_do(struct tevent_req *subreq)
321 {
322         struct tevent_req *req = tevent_req_callback_data(
323                 subreq, struct tevent_req);
324         struct smbd_smb2_close_state *state = tevent_req_data(
325                 req, struct smbd_smb2_close_state);
326         NTSTATUS status;
327         int ret;
328
329         ret = tevent_wait_recv(subreq);
330         TALLOC_FREE(subreq);
331         if (ret != 0) {
332                 DEBUG(10, ("tevent_wait_recv returned %s\n",
333                            strerror(ret)));
334                 /*
335                  * Continue anyway, this should never happen
336                  */
337         }
338
339         status = smbd_smb2_close(state->smb2req,
340                                  state->in_fsp,
341                                  state->in_flags,
342                                  &state->out_flags,
343                                  &state->out_creation_time,
344                                  &state->out_last_access_time,
345                                  &state->out_last_write_time,
346                                  &state->out_change_time,
347                                  &state->out_allocation_size,
348                                  &state->out_end_of_file,
349                                  &state->out_file_attributes);
350         if (tevent_req_nterror(req, status)) {
351                 return;
352         }
353         tevent_req_done(req);
354 }
355
356 static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
357                                      uint16_t *out_flags,
358                                      NTTIME *out_creation_time,
359                                      NTTIME *out_last_access_time,
360                                      NTTIME *out_last_write_time,
361                                      NTTIME *out_change_time,
362                                      uint64_t *out_allocation_size,
363                                      uint64_t *out_end_of_file,
364                                      uint32_t *out_file_attributes)
365 {
366         struct smbd_smb2_close_state *state =
367                 tevent_req_data(req,
368                 struct smbd_smb2_close_state);
369         NTSTATUS status;
370
371         if (tevent_req_is_nterror(req, &status)) {
372                 tevent_req_received(req);
373                 return status;
374         }
375
376         *out_flags = state->out_flags;
377         *out_creation_time = state->out_creation_time;
378         *out_last_access_time = state->out_last_access_time;
379         *out_last_write_time = state->out_last_write_time;
380         *out_change_time = state->out_change_time;
381         *out_allocation_size = state->out_allocation_size;
382         *out_end_of_file = state->out_end_of_file;
383         *out_file_attributes = state->out_file_attributes;
384
385         tevent_req_received(req);
386         return NT_STATUS_OK;
387 }