s3:smbd: split out smbd_do_qfsinfo() from call_trans2qfsinfo()
[Samba/fernandojvsilva.git] / source3 / smbd / smb2_notify.c
blob7ab93ce574b368657f89043432cb19e8c7223d6f
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/globals.h"
23 #include "../source4/libcli/smb2/smb2_constants.h"
25 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
26 struct tevent_context *ev,
27 struct smbd_smb2_request *smb2req,
28 uint16_t in_flags,
29 uint32_t in_output_buffer_length,
30 uint64_t in_file_id_volatile,
31 uint64_t in_completion_filter);
32 static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
33 TALLOC_CTX *mem_ctx,
34 DATA_BLOB *out_output_buffer);
36 static void smbd_smb2_request_notify_done(struct tevent_req *subreq);
37 NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
39 const uint8_t *inhdr;
40 const uint8_t *inbody;
41 int i = req->current_idx;
42 size_t expected_body_size = 0x20;
43 size_t body_size;
44 uint16_t in_flags;
45 uint32_t in_output_buffer_length;
46 uint64_t in_file_id_persistent;
47 uint64_t in_file_id_volatile;
48 uint64_t in_completion_filter;
49 struct tevent_req *subreq;
51 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
52 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
53 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
56 inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
58 body_size = SVAL(inbody, 0x00);
59 if (body_size != expected_body_size) {
60 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
63 in_flags = SVAL(inbody, 0x02);
64 in_output_buffer_length = IVAL(inbody, 0x04);
65 in_file_id_persistent = BVAL(inbody, 0x08);
66 in_file_id_volatile = BVAL(inbody, 0x10);
67 in_completion_filter = IVAL(inbody, 0x18);
70 * 0x00010000 is what Windows 7 uses,
71 * Windows 2008 uses 0x00080000
73 if (in_output_buffer_length > 0x00010000) {
74 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
77 if (req->compat_chain_fsp) {
78 /* skip check */
79 } else if (in_file_id_persistent != 0) {
80 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
83 subreq = smbd_smb2_notify_send(req,
84 req->conn->smb2.event_ctx,
85 req,
86 in_flags,
87 in_output_buffer_length,
88 in_file_id_volatile,
89 in_completion_filter);
90 if (subreq == NULL) {
91 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
93 tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
95 if (tevent_req_is_in_progress(subreq)) {
96 return smbd_smb2_request_pending_queue(req);
99 return NT_STATUS_OK;
102 static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
104 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
105 struct smbd_smb2_request);
106 int i = req->current_idx;
107 uint8_t *outhdr;
108 DATA_BLOB outbody;
109 DATA_BLOB outdyn;
110 uint16_t out_output_buffer_offset;
111 DATA_BLOB out_output_buffer;
112 NTSTATUS status;
113 NTSTATUS error; /* transport error */
115 status = smbd_smb2_notify_recv(subreq,
116 req,
117 &out_output_buffer);
118 TALLOC_FREE(subreq);
119 if (!NT_STATUS_IS_OK(status)) {
120 error = smbd_smb2_request_error(req, status);
121 if (!NT_STATUS_IS_OK(error)) {
122 smbd_server_connection_terminate(req->conn,
123 nt_errstr(error));
124 return;
126 return;
129 out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
131 outhdr = (uint8_t *)req->out.vector[i].iov_base;
133 outbody = data_blob_talloc(req->out.vector, NULL, 0x08);
134 if (outbody.data == NULL) {
135 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
136 if (!NT_STATUS_IS_OK(error)) {
137 smbd_server_connection_terminate(req->conn,
138 nt_errstr(error));
139 return;
141 return;
144 SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
145 SSVAL(outbody.data, 0x02,
146 out_output_buffer_offset); /* output buffer offset */
147 SIVAL(outbody.data, 0x04,
148 out_output_buffer.length); /* output buffer length */
150 outdyn = out_output_buffer;
152 error = smbd_smb2_request_done(req, outbody, &outdyn);
153 if (!NT_STATUS_IS_OK(error)) {
154 smbd_server_connection_terminate(req->conn,
155 nt_errstr(error));
156 return;
160 struct smbd_smb2_notify_state {
161 struct smbd_smb2_request *smb2req;
162 struct tevent_immediate *im;
163 NTSTATUS status;
164 DATA_BLOB out_output_buffer;
167 static void smbd_smb2_notify_reply(struct smb_request *smbreq,
168 NTSTATUS error_code,
169 uint8_t *buf, size_t len);
170 static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx,
171 struct tevent_immediate *im,
172 void *private_data);
174 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
175 struct tevent_context *ev,
176 struct smbd_smb2_request *smb2req,
177 uint16_t in_flags,
178 uint32_t in_output_buffer_length,
179 uint64_t in_file_id_volatile,
180 uint64_t in_completion_filter)
182 struct tevent_req *req;
183 struct smbd_smb2_notify_state *state;
184 struct smb_request *smbreq;
185 connection_struct *conn = smb2req->tcon->compat_conn;
186 files_struct *fsp;
187 bool recursive = (in_flags & 0x0001) ? true : false;
188 NTSTATUS status;
190 req = tevent_req_create(mem_ctx, &state,
191 struct smbd_smb2_notify_state);
192 if (req == NULL) {
193 return NULL;
195 state->smb2req = smb2req;
196 state->status = NT_STATUS_INTERNAL_ERROR;
197 state->out_output_buffer = data_blob_null;
198 state->im = NULL;
200 DEBUG(10,("smbd_smb2_notify_send: file_id[0x%016llX]\n",
201 (unsigned long long)in_file_id_volatile));
203 smbreq = smbd_smb2_fake_smb_request(smb2req);
204 if (tevent_req_nomem(smbreq, req)) {
205 return tevent_req_post(req, ev);
208 smbreq->async_priv = (void *)req;
210 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
211 if (fsp == NULL) {
212 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
213 return tevent_req_post(req, ev);
215 if (conn != fsp->conn) {
216 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
217 return tevent_req_post(req, ev);
219 if (smb2req->session->vuid != fsp->vuid) {
220 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
221 return tevent_req_post(req, ev);
225 char *filter_string;
227 filter_string = notify_filter_string(NULL, in_completion_filter);
228 if (tevent_req_nomem(filter_string, req)) {
229 return tevent_req_post(req, ev);
232 DEBUG(3,("smbd_smb2_notify_send: notify change "
233 "called on %s, filter = %s, recursive = %d\n",
234 fsp->fsp_name, filter_string, recursive));
236 TALLOC_FREE(filter_string);
239 if ((!fsp->is_directory) || (conn != fsp->conn)) {
240 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
241 return tevent_req_post(req, ev);
244 if (fsp->notify == NULL) {
246 status = change_notify_create(fsp,
247 in_completion_filter,
248 recursive);
249 if (!NT_STATUS_IS_OK(status)) {
250 DEBUG(10, ("change_notify_create returned %s\n",
251 nt_errstr(status)));
252 tevent_req_nterror(req, status);
253 return tevent_req_post(req, ev);
257 if (fsp->notify->num_changes != 0) {
260 * We've got changes pending, respond immediately
264 * TODO: write a torture test to check the filtering behaviour
265 * here.
268 change_notify_reply(fsp->conn, smbreq,
269 NT_STATUS_OK,
270 in_output_buffer_length,
271 fsp->notify,
272 smbd_smb2_notify_reply);
275 * change_notify_reply() above has independently
276 * called tevent_req_done().
278 return tevent_req_post(req, ev);
281 state->im = tevent_create_immediate(state);
282 if (tevent_req_nomem(state->im, req)) {
283 return tevent_req_post(req, ev);
287 * No changes pending, queue the request
290 status = change_notify_add_request(smbreq,
291 in_output_buffer_length,
292 in_completion_filter,
293 recursive, fsp,
294 smbd_smb2_notify_reply);
295 if (!NT_STATUS_IS_OK(status)) {
296 tevent_req_nterror(req, status);
297 return tevent_req_post(req, ev);
300 return req;
303 static void smbd_smb2_notify_reply(struct smb_request *smbreq,
304 NTSTATUS error_code,
305 uint8_t *buf, size_t len)
307 struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
308 struct tevent_req);
309 struct smbd_smb2_notify_state *state = tevent_req_data(req,
310 struct smbd_smb2_notify_state);
312 state->status = error_code;
313 if (!NT_STATUS_IS_OK(error_code)) {
314 /* nothing */
315 } else if (len == 0) {
316 state->status = STATUS_NOTIFY_ENUM_DIR;
317 } else {
318 state->out_output_buffer = data_blob_talloc(state, buf, len);
319 if (state->out_output_buffer.data == NULL) {
320 state->status = NT_STATUS_NO_MEMORY;
324 if (state->im == NULL) {
325 smbd_smb2_notify_reply_trigger(NULL, NULL, req);
326 return;
330 * if this is called async, we need to go via an immediate event
331 * because the caller replies on the smb_request (a child of req
332 * being arround after calling this function
334 tevent_schedule_immediate(state->im,
335 state->smb2req->conn->smb2.event_ctx,
336 smbd_smb2_notify_reply_trigger,
337 req);
340 static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx,
341 struct tevent_immediate *im,
342 void *private_data)
344 struct tevent_req *req = talloc_get_type_abort(private_data,
345 struct tevent_req);
346 struct smbd_smb2_notify_state *state = tevent_req_data(req,
347 struct smbd_smb2_notify_state);
349 if (!NT_STATUS_IS_OK(state->status)) {
350 tevent_req_nterror(req, state->status);
351 return;
354 tevent_req_done(req);
357 static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
358 TALLOC_CTX *mem_ctx,
359 DATA_BLOB *out_output_buffer)
361 NTSTATUS status;
362 struct smbd_smb2_notify_state *state = tevent_req_data(req,
363 struct smbd_smb2_notify_state);
365 if (tevent_req_is_nterror(req, &status)) {
366 tevent_req_received(req);
367 return status;
370 *out_output_buffer = state->out_output_buffer;
371 talloc_steal(mem_ctx, out_output_buffer->data);
373 tevent_req_received(req);
374 return NT_STATUS_OK;