s3:smbd: Don't rename a dir with files open underneath
[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 NTSTATUS smbd_smb2_close(struct smbd_smb2_request *req,
153                                 struct files_struct *fsp,
154                                 uint16_t in_flags,
155                                 uint16_t *out_flags,
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                                 uint64_t *out_allocation_size,
161                                 uint64_t *out_end_of_file,
162                                 uint32_t *out_file_attributes)
163 {
164         NTSTATUS status;
165         struct smb_request *smbreq;
166         connection_struct *conn = req->tcon->compat;
167         struct smb_filename *smb_fname = NULL;
168         uint64_t allocation_size = 0;
169         uint64_t file_size = 0;
170         uint32_t dos_attrs = 0;
171         uint16_t flags = 0;
172         bool posix_open = false;
173
174         ZERO_STRUCTP(out_creation_ts);
175         ZERO_STRUCTP(out_last_access_ts);
176         ZERO_STRUCTP(out_last_write_ts);
177         ZERO_STRUCTP(out_change_ts);
178
179         *out_flags = 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                         *out_last_write_ts = smb_fname->st.st_ex_mtime;
216                         *out_last_access_ts = smb_fname->st.st_ex_atime;
217                         *out_creation_ts = get_create_timespec(conn, NULL, smb_fname);
218                         *out_change_ts = get_change_timespec(conn, NULL, smb_fname);
219
220                         if (lp_dos_filetime_resolution(SNUM(conn))) {
221                                 dos_filetime_timespec(out_creation_ts);
222                                 dos_filetime_timespec(out_last_write_ts);
223                                 dos_filetime_timespec(out_last_access_ts);
224                                 dos_filetime_timespec(out_change_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         *out_allocation_size = allocation_size;
236         *out_end_of_file = file_size;
237         *out_file_attributes = dos_attrs;
238
239         return NT_STATUS_OK;
240 }
241
242 struct smbd_smb2_close_state {
243         struct smbd_smb2_request *smb2req;
244         struct files_struct *in_fsp;
245         uint16_t in_flags;
246         uint16_t out_flags;
247         struct timespec out_creation_ts;
248         struct timespec out_last_access_ts;
249         struct timespec out_last_write_ts;
250         struct timespec out_change_ts;
251         uint64_t out_allocation_size;
252         uint64_t out_end_of_file;
253         uint32_t out_file_attributes;
254 };
255
256 static void smbd_smb2_close_do(struct tevent_req *subreq);
257
258 static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
259                                                struct tevent_context *ev,
260                                                struct smbd_smb2_request *smb2req,
261                                                struct files_struct *in_fsp,
262                                                uint16_t in_flags)
263 {
264         struct tevent_req *req;
265         struct smbd_smb2_close_state *state;
266         NTSTATUS status;
267
268         req = tevent_req_create(mem_ctx, &state,
269                                 struct smbd_smb2_close_state);
270         if (req == NULL) {
271                 return NULL;
272         }
273         state->smb2req = smb2req;
274         state->in_fsp = in_fsp;
275         state->in_flags = in_flags;
276
277         if (in_fsp->num_aio_requests != 0) {
278
279                 in_fsp->deferred_close = tevent_wait_send(in_fsp, ev);
280                 if (tevent_req_nomem(in_fsp->deferred_close, req)) {
281                         return tevent_req_post(req, ev);
282                 }
283                 tevent_req_set_callback(in_fsp->deferred_close,
284                                         smbd_smb2_close_do, req);
285                 return req;
286         }
287
288         status = smbd_smb2_close(smb2req,
289                                  state->in_fsp,
290                                  state->in_flags,
291                                  &state->out_flags,
292                                  &state->out_creation_ts,
293                                  &state->out_last_access_ts,
294                                  &state->out_last_write_ts,
295                                  &state->out_change_ts,
296                                  &state->out_allocation_size,
297                                  &state->out_end_of_file,
298                                  &state->out_file_attributes);
299         if (tevent_req_nterror(req, status)) {
300                 return tevent_req_post(req, ev);
301         }
302
303         tevent_req_done(req);
304         return tevent_req_post(req, ev);
305 }
306
307 static void smbd_smb2_close_do(struct tevent_req *subreq)
308 {
309         struct tevent_req *req = tevent_req_callback_data(
310                 subreq, struct tevent_req);
311         struct smbd_smb2_close_state *state = tevent_req_data(
312                 req, struct smbd_smb2_close_state);
313         NTSTATUS status;
314         int ret;
315
316         ret = tevent_wait_recv(subreq);
317         TALLOC_FREE(subreq);
318         if (ret != 0) {
319                 DEBUG(10, ("tevent_wait_recv returned %s\n",
320                            strerror(ret)));
321                 /*
322                  * Continue anyway, this should never happen
323                  */
324         }
325
326         status = smbd_smb2_close(state->smb2req,
327                                  state->in_fsp,
328                                  state->in_flags,
329                                  &state->out_flags,
330                                  &state->out_creation_ts,
331                                  &state->out_last_access_ts,
332                                  &state->out_last_write_ts,
333                                  &state->out_change_ts,
334                                  &state->out_allocation_size,
335                                  &state->out_end_of_file,
336                                  &state->out_file_attributes);
337         if (tevent_req_nterror(req, status)) {
338                 return;
339         }
340         tevent_req_done(req);
341 }
342
343 static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
344                                      uint16_t *out_flags,
345                                      struct timespec *out_creation_ts,
346                                      struct timespec *out_last_access_ts,
347                                      struct timespec *out_last_write_ts,
348                                      struct timespec *out_change_ts,
349                                      uint64_t *out_allocation_size,
350                                      uint64_t *out_end_of_file,
351                                      uint32_t *out_file_attributes)
352 {
353         struct smbd_smb2_close_state *state =
354                 tevent_req_data(req,
355                 struct smbd_smb2_close_state);
356         NTSTATUS status;
357
358         if (tevent_req_is_nterror(req, &status)) {
359                 tevent_req_received(req);
360                 return status;
361         }
362
363         *out_flags = state->out_flags;
364         *out_creation_ts = state->out_creation_ts;
365         *out_last_access_ts = state->out_last_access_ts;
366         *out_last_write_ts = state->out_last_write_ts;
367         *out_change_ts = state->out_change_ts;
368         *out_allocation_size = state->out_allocation_size;
369         *out_end_of_file = state->out_end_of_file;
370         *out_file_attributes = state->out_file_attributes;
371
372         tevent_req_received(req);
373         return NT_STATUS_OK;
374 }