2 Unix SMB/CIFS implementation.
5 Copyright (C) Stefan Metzmacher 2009
6 Copyright (C) Jeremy Allison 2010
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "../libcli/smb/smb_common.h"
26 #include "../lib/util/tevent_ntstatus.h"
28 struct smbd_smb2_notify_state {
29 struct smbd_smb2_request *smb2req;
30 struct smb_request *smbreq;
32 DATA_BLOB out_output_buffer;
35 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
36 struct tevent_context *ev,
37 struct smbd_smb2_request *smb2req,
38 struct files_struct *in_fsp,
40 uint32_t in_output_buffer_length,
41 uint64_t in_completion_filter);
42 static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
44 DATA_BLOB *out_output_buffer);
46 static void smbd_smb2_request_notify_done(struct tevent_req *subreq);
47 NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
50 const uint8_t *inbody;
52 uint32_t in_output_buffer_length;
53 uint64_t in_file_id_persistent;
54 uint64_t in_file_id_volatile;
55 struct files_struct *in_fsp;
56 uint64_t in_completion_filter;
57 struct tevent_req *subreq;
59 status = smbd_smb2_request_verify_sizes(req, 0x20);
60 if (!NT_STATUS_IS_OK(status)) {
61 return smbd_smb2_request_error(req, status);
63 inbody = SMBD_SMB2_IN_BODY_PTR(req);
65 in_flags = SVAL(inbody, 0x02);
66 in_output_buffer_length = IVAL(inbody, 0x04);
67 in_file_id_persistent = BVAL(inbody, 0x08);
68 in_file_id_volatile = BVAL(inbody, 0x10);
69 in_completion_filter = IVAL(inbody, 0x18);
72 * 0x00010000 is what Windows 7 uses,
73 * Windows 2008 uses 0x00080000
75 if (in_output_buffer_length > req->sconn->smb2.max_trans) {
76 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
79 status = smbd_smb2_request_verify_creditcharge(req,
80 in_output_buffer_length);
82 if (!NT_STATUS_IS_OK(status)) {
83 return smbd_smb2_request_error(req, status);
86 in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
88 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
91 subreq = smbd_smb2_notify_send(req, req->sconn->ev_ctx,
94 in_output_buffer_length,
95 in_completion_filter);
97 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
99 tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
101 return smbd_smb2_request_pending_queue(req, subreq, 500);
104 static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
106 struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
107 struct smbd_smb2_request);
110 uint16_t out_output_buffer_offset;
111 DATA_BLOB out_output_buffer = data_blob_null;
113 NTSTATUS error; /* transport error */
115 if (req->cancelled) {
116 struct smbd_smb2_notify_state *state = tevent_req_data(subreq,
117 struct smbd_smb2_notify_state);
118 const uint8_t *inhdr = SMBD_SMB2_IN_HDR_PTR(req);
119 uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
121 DEBUG(10,("smbd_smb2_request_notify_done: cancelled mid %llu\n",
122 (unsigned long long)mid ));
123 error = smbd_smb2_request_error(req, NT_STATUS_CANCELLED);
124 if (!NT_STATUS_IS_OK(error)) {
125 smbd_server_connection_terminate(req->sconn,
129 TALLOC_FREE(state->im);
133 status = smbd_smb2_notify_recv(subreq,
137 if (!NT_STATUS_IS_OK(status)) {
138 error = smbd_smb2_request_error(req, status);
139 if (!NT_STATUS_IS_OK(error)) {
140 smbd_server_connection_terminate(req->sconn,
147 out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
149 outbody = data_blob_talloc(req->out.vector, NULL, 0x08);
150 if (outbody.data == NULL) {
151 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
152 if (!NT_STATUS_IS_OK(error)) {
153 smbd_server_connection_terminate(req->sconn,
160 SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
161 SSVAL(outbody.data, 0x02,
162 out_output_buffer_offset); /* output buffer offset */
163 SIVAL(outbody.data, 0x04,
164 out_output_buffer.length); /* output buffer length */
166 outdyn = out_output_buffer;
168 error = smbd_smb2_request_done(req, outbody, &outdyn);
169 if (!NT_STATUS_IS_OK(error)) {
170 smbd_server_connection_terminate(req->sconn,
176 static void smbd_smb2_notify_reply(struct smb_request *smbreq,
178 uint8_t *buf, size_t len);
179 static bool smbd_smb2_notify_cancel(struct tevent_req *req);
181 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
182 struct tevent_context *ev,
183 struct smbd_smb2_request *smb2req,
184 struct files_struct *fsp,
186 uint32_t in_output_buffer_length,
187 uint64_t in_completion_filter)
189 struct tevent_req *req;
190 struct smbd_smb2_notify_state *state;
191 struct smb_request *smbreq;
192 connection_struct *conn = smb2req->tcon->compat;
193 bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false;
196 req = tevent_req_create(mem_ctx, &state,
197 struct smbd_smb2_notify_state);
201 state->smb2req = smb2req;
202 state->status = NT_STATUS_INTERNAL_ERROR;
203 state->out_output_buffer = data_blob_null;
205 DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
206 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
208 smbreq = smbd_smb2_fake_smb_request(smb2req);
209 if (tevent_req_nomem(smbreq, req)) {
210 return tevent_req_post(req, ev);
213 state->smbreq = smbreq;
214 smbreq->async_priv = (void *)req;
219 filter_string = notify_filter_string(NULL, in_completion_filter);
220 if (tevent_req_nomem(filter_string, req)) {
221 return tevent_req_post(req, ev);
224 DEBUG(3,("smbd_smb2_notify_send: notify change "
225 "called on %s, filter = %s, recursive = %d\n",
226 fsp_str_dbg(fsp), filter_string, recursive));
228 TALLOC_FREE(filter_string);
231 if ((!fsp->is_directory) || (conn != fsp->conn)) {
232 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
233 return tevent_req_post(req, ev);
236 if (fsp->notify == NULL) {
238 status = change_notify_create(fsp,
239 in_completion_filter,
241 if (!NT_STATUS_IS_OK(status)) {
242 DEBUG(10, ("change_notify_create returned %s\n",
244 tevent_req_nterror(req, status);
245 return tevent_req_post(req, ev);
249 if (change_notify_fsp_has_changes(fsp)) {
252 * We've got changes pending, respond immediately
256 * TODO: write a torture test to check the filtering behaviour
260 change_notify_reply(smbreq,
262 in_output_buffer_length,
264 smbd_smb2_notify_reply);
267 * change_notify_reply() above has independently
268 * called tevent_req_done().
270 return tevent_req_post(req, ev);
274 * No changes pending, queue the request
277 status = change_notify_add_request(smbreq,
278 in_output_buffer_length,
279 in_completion_filter,
281 smbd_smb2_notify_reply);
282 if (!NT_STATUS_IS_OK(status)) {
283 tevent_req_nterror(req, status);
284 return tevent_req_post(req, ev);
287 /* allow this request to be canceled */
288 tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
293 static void smbd_smb2_notify_reply(struct smb_request *smbreq,
295 uint8_t *buf, size_t len)
297 struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
299 struct smbd_smb2_notify_state *state = tevent_req_data(req,
300 struct smbd_smb2_notify_state);
302 state->status = error_code;
303 if (!NT_STATUS_IS_OK(error_code)) {
305 } else if (len == 0) {
306 state->status = STATUS_NOTIFY_ENUM_DIR;
308 state->out_output_buffer = data_blob_talloc(state, buf, len);
309 if (state->out_output_buffer.data == NULL) {
310 state->status = NT_STATUS_NO_MEMORY;
314 tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx);
316 if (!NT_STATUS_IS_OK(state->status)) {
317 tevent_req_nterror(req, state->status);
321 tevent_req_done(req);
324 static bool smbd_smb2_notify_cancel(struct tevent_req *req)
326 struct smbd_smb2_notify_state *state = tevent_req_data(req,
327 struct smbd_smb2_notify_state);
329 state->smb2req->cancelled = true;
330 smbd_notify_cancel_by_smbreq(state->smbreq);
335 static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
337 DATA_BLOB *out_output_buffer)
340 struct smbd_smb2_notify_state *state = tevent_req_data(req,
341 struct smbd_smb2_notify_state);
343 if (tevent_req_is_nterror(req, &status)) {
344 tevent_req_received(req);
348 *out_output_buffer = state->out_output_buffer;
349 talloc_steal(mem_ctx, out_output_buffer->data);
351 tevent_req_received(req);