Merge branch 'master' of ssh://jra@git.samba.org/data/git/samba
[kai/samba-autobuild/.git] / source3 / smbd / notify.c
1 /*
2    Unix SMB/CIFS implementation.
3    change notify handling
4    Copyright (C) Andrew Tridgell 2000
5    Copyright (C) Jeremy Allison 1994-1998
6    Copyright (C) Volker Lendecke 2007
7
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.
12
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.
17
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/>.
20 */
21
22 #include "includes.h"
23
24 struct notify_change_request {
25         struct notify_change_request *prev, *next;
26         struct files_struct *fsp;       /* backpointer for cancel by mid */
27         struct smb_request *req;
28         uint32 filter;
29         uint32 max_param;
30         struct notify_mid_map *mid_map;
31         void *backend_data;
32 };
33
34 static void notify_fsp(files_struct *fsp, uint32 action, const char *name);
35
36 static struct notify_mid_map *notify_changes_by_mid;
37
38 /*
39  * For NTCancel, we need to find the notify_change_request indexed by
40  * mid. Separate list here.
41  */
42
43 struct notify_mid_map {
44         struct notify_mid_map *prev, *next;
45         struct notify_change_request *req;
46         uint16 mid;
47 };
48
49 static bool notify_change_record_identical(struct notify_change *c1,
50                                         struct notify_change *c2)
51 {
52         /* Note this is deliberately case sensitive. */
53         if (c1->action == c2->action &&
54                         strcmp(c1->name, c2->name) == 0) {
55                 return True;
56         }
57         return False;
58 }
59
60 static bool notify_marshall_changes(int num_changes,
61                                 uint32 max_offset,
62                                 struct notify_change *changes,
63                                 prs_struct *ps)
64 {
65         int i;
66         UNISTR uni_name;
67
68         uni_name.buffer = NULL;
69
70         for (i=0; i<num_changes; i++) {
71                 struct notify_change *c;
72                 size_t namelen;
73                 uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid
74                                  * signed/unsigned issues */
75
76                 /* Coalesce any identical records. */
77                 while (i+1 < num_changes &&
78                         notify_change_record_identical(&changes[i],
79                                                 &changes[i+1])) {
80                         i++;
81                 }
82
83                 c = &changes[i];
84
85                 if (!convert_string_allocate(NULL, CH_UNIX, CH_UTF16LE,
86                         c->name, strlen(c->name)+1, &uni_name.buffer,
87                         &namelen, True) || (uni_name.buffer == NULL)) {
88                         goto fail;
89                 }
90
91                 namelen -= 2;   /* Dump NULL termination */
92
93                 /*
94                  * Offset to next entry, only if there is one
95                  */
96
97                 u32_tmp = (i == num_changes-1) ? 0 : namelen + 12;
98                 if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail;
99
100                 u32_tmp = c->action;
101                 if (!prs_uint32("action", ps, 1, &u32_tmp)) goto fail;
102
103                 u32_tmp = namelen;
104                 if (!prs_uint32("namelen", ps, 1, &u32_tmp)) goto fail;
105
106                 if (!prs_unistr("name", ps, 1, &uni_name)) goto fail;
107
108                 /*
109                  * Not NULL terminated, decrease by the 2 UCS2 \0 chars
110                  */
111                 prs_set_offset(ps, prs_offset(ps)-2);
112
113                 SAFE_FREE(uni_name.buffer);
114
115                 if (prs_offset(ps) > max_offset) {
116                         /* Too much data for client. */
117                         DEBUG(10, ("Client only wanted %d bytes, trying to "
118                                    "marshall %d bytes\n", (int)max_offset,
119                                    (int)prs_offset(ps)));
120                         return False;
121                 }
122         }
123
124         return True;
125
126  fail:
127         SAFE_FREE(uni_name.buffer);
128         return False;
129 }
130
131 /****************************************************************************
132  Setup the common parts of the return packet and send it.
133 *****************************************************************************/
134
135 static void change_notify_reply_packet(connection_struct *conn,
136                                        struct smb_request *req,
137                                        NTSTATUS error_code)
138 {
139         reply_outbuf(req, 18, 0);
140
141         if (!NT_STATUS_IS_OK(error_code)) {
142                 error_packet_set((char *)req->outbuf, 0, 0, error_code,
143                                  __LINE__,__FILE__);
144         }
145
146         show_msg((char *)req->outbuf);
147         if (!srv_send_smb(smbd_server_fd(), (char *)req->outbuf,
148                           req->encrypted)) {
149                 exit_server_cleanly("change_notify_reply_packet: srv_send_smb "
150                                     "failed.");
151         }
152         TALLOC_FREE(req->outbuf);
153 }
154
155 void change_notify_reply(connection_struct *conn,
156                          struct smb_request *req, uint32 max_param,
157                          struct notify_change_buf *notify_buf)
158 {
159         prs_struct ps;
160
161         if (notify_buf->num_changes == -1) {
162                 change_notify_reply_packet(conn, req, NT_STATUS_OK);
163                 notify_buf->num_changes = 0;
164                 return;
165         }
166
167         prs_init_empty(&ps, NULL, MARSHALL);
168
169         if (!notify_marshall_changes(notify_buf->num_changes, max_param,
170                                         notify_buf->changes, &ps)) {
171                 /*
172                  * We exceed what the client is willing to accept. Send
173                  * nothing.
174                  */
175                 change_notify_reply_packet(conn, req, NT_STATUS_OK);
176                 goto done;
177         }
178
179         send_nt_replies(conn, req, NT_STATUS_OK, prs_data_p(&ps),
180                         prs_offset(&ps), NULL, 0);
181
182  done:
183         prs_mem_free(&ps);
184
185         TALLOC_FREE(notify_buf->changes);
186         notify_buf->num_changes = 0;
187 }
188
189 static void notify_callback(void *private_data, const struct notify_event *e)
190 {
191         files_struct *fsp = (files_struct *)private_data;
192         DEBUG(10, ("notify_callback called for %s\n", fsp->fsp_name));
193         notify_fsp(fsp, e->action, e->path);
194 }
195
196 NTSTATUS change_notify_create(struct files_struct *fsp, uint32 filter,
197                               bool recursive)
198 {
199         char *fullpath;
200         struct notify_entry e;
201         NTSTATUS status;
202
203         SMB_ASSERT(fsp->notify == NULL);
204
205         if (!(fsp->notify = TALLOC_ZERO_P(NULL, struct notify_change_buf))) {
206                 DEBUG(0, ("talloc failed\n"));
207                 return NT_STATUS_NO_MEMORY;
208         }
209
210         if (asprintf(&fullpath, "%s/%s", fsp->conn->connectpath,
211                      fsp->fsp_name) == -1) {
212                 DEBUG(0, ("asprintf failed\n"));
213                 return NT_STATUS_NO_MEMORY;
214         }
215
216         ZERO_STRUCT(e);
217         e.path = fullpath;
218         e.filter = filter;
219         e.subdir_filter = 0;
220         if (recursive) {
221                 e.subdir_filter = filter;
222         }
223
224         status = notify_add(fsp->conn->notify_ctx, &e, notify_callback, fsp);
225         SAFE_FREE(fullpath);
226
227         return status;
228 }
229
230 NTSTATUS change_notify_add_request(struct smb_request *req,
231                                 uint32 max_param,
232                                 uint32 filter, bool recursive,
233                                 struct files_struct *fsp)
234 {
235         struct notify_change_request *request = NULL;
236         struct notify_mid_map *map = NULL;
237
238         DEBUG(10, ("change_notify_add_request: Adding request for %s: "
239                    "max_param = %d\n", fsp->fsp_name, (int)max_param));
240
241         if (!(request = talloc(NULL, struct notify_change_request))
242             || !(map = talloc(request, struct notify_mid_map))) {
243                 TALLOC_FREE(request);
244                 return NT_STATUS_NO_MEMORY;
245         }
246
247         request->mid_map = map;
248         map->req = request;
249
250         request->req = talloc_move(request, &req);
251         request->max_param = max_param;
252         request->filter = filter;
253         request->fsp = fsp;
254         request->backend_data = NULL;
255
256         DLIST_ADD_END(fsp->notify->requests, request,
257                       struct notify_change_request *);
258
259         map->mid = request->req->mid;
260         DLIST_ADD(notify_changes_by_mid, map);
261
262         /* Push the MID of this packet on the signing queue. */
263         srv_defer_sign_response(request->req->mid);
264
265         return NT_STATUS_OK;
266 }
267
268 static void change_notify_remove_request(struct notify_change_request *remove_req)
269 {
270         files_struct *fsp;
271         struct notify_change_request *req;
272
273         /*
274          * Paranoia checks, the fsp referenced must must have the request in
275          * its list of pending requests
276          */
277
278         fsp = remove_req->fsp;
279         SMB_ASSERT(fsp->notify != NULL);
280
281         for (req = fsp->notify->requests; req; req = req->next) {
282                 if (req == remove_req) {
283                         break;
284                 }
285         }
286
287         if (req == NULL) {
288                 smb_panic("notify_req not found in fsp's requests");
289         }
290
291         DLIST_REMOVE(fsp->notify->requests, req);
292         DLIST_REMOVE(notify_changes_by_mid, req->mid_map);
293         TALLOC_FREE(req);
294 }
295
296 /****************************************************************************
297  Delete entries by mid from the change notify pending queue. Always send reply.
298 *****************************************************************************/
299
300 void remove_pending_change_notify_requests_by_mid(uint16 mid)
301 {
302         struct notify_mid_map *map;
303
304         for (map = notify_changes_by_mid; map; map = map->next) {
305                 if (map->mid == mid) {
306                         break;
307                 }
308         }
309
310         if (map == NULL) {
311                 return;
312         }
313
314         change_notify_reply_packet(map->req->fsp->conn, map->req->req,
315                                    NT_STATUS_CANCELLED);
316         change_notify_remove_request(map->req);
317 }
318
319 /****************************************************************************
320  Delete entries by fnum from the change notify pending queue.
321 *****************************************************************************/
322
323 void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
324                                                   NTSTATUS status)
325 {
326         if (fsp->notify == NULL) {
327                 return;
328         }
329
330         while (fsp->notify->requests != NULL) {
331                 change_notify_reply_packet(
332                         fsp->conn, fsp->notify->requests->req, status);
333                 change_notify_remove_request(fsp->notify->requests);
334         }
335 }
336
337 void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
338                   const char *path)
339 {
340         char *fullpath;
341
342         if (asprintf(&fullpath, "%s/%s", conn->connectpath, path) == -1) {
343                 DEBUG(0, ("asprintf failed\n"));
344                 return;
345         }
346
347         notify_trigger(conn->notify_ctx, action, filter, fullpath);
348         SAFE_FREE(fullpath);
349 }
350
351 static void notify_fsp(files_struct *fsp, uint32 action, const char *name)
352 {
353         struct notify_change *change, *changes;
354         char *tmp;
355
356         if (fsp->notify == NULL) {
357                 /*
358                  * Nobody is waiting, don't queue
359                  */
360                 return;
361         }
362
363         /*
364          * Someone has triggered a notify previously, queue the change for
365          * later.
366          */
367
368         if ((fsp->notify->num_changes > 1000) || (name == NULL)) {
369                 /*
370                  * The real number depends on the client buf, just provide a
371                  * guard against a DoS here.
372                  */
373                 TALLOC_FREE(fsp->notify->changes);
374                 fsp->notify->num_changes = -1;
375                 return;
376         }
377
378         if (fsp->notify->num_changes == -1) {
379                 return;
380         }
381
382         if (!(changes = TALLOC_REALLOC_ARRAY(
383                       fsp->notify, fsp->notify->changes,
384                       struct notify_change, fsp->notify->num_changes+1))) {
385                 DEBUG(0, ("talloc_realloc failed\n"));
386                 return;
387         }
388
389         fsp->notify->changes = changes;
390
391         change = &(fsp->notify->changes[fsp->notify->num_changes]);
392
393         if (!(tmp = talloc_strdup(changes, name))) {
394                 DEBUG(0, ("talloc_strdup failed\n"));
395                 return;
396         }
397
398         string_replace(tmp, '/', '\\');
399         change->name = tmp;     
400
401         change->action = action;
402         fsp->notify->num_changes += 1;
403
404         if (fsp->notify->requests == NULL) {
405                 /*
406                  * Nobody is waiting, so don't send anything. The ot
407                  */
408                 return;
409         }
410
411         if (action == NOTIFY_ACTION_OLD_NAME) {
412                 /*
413                  * We have to send the two rename events in one reply. So hold
414                  * the first part back.
415                  */
416                 return;
417         }
418
419         /*
420          * Someone is waiting for the change, trigger the reply immediately.
421          *
422          * TODO: do we have to walk the lists of requests pending?
423          */
424
425         change_notify_reply(fsp->conn,
426                             fsp->notify->requests->req,
427                             fsp->notify->requests->max_param,
428                             fsp->notify);
429
430         change_notify_remove_request(fsp->notify->requests);
431 }
432
433 char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter)
434 {
435         char *result = NULL;
436
437         result = talloc_strdup(mem_ctx, "");
438
439         if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
440                 result = talloc_asprintf_append(result, "FILE_NAME|");
441         if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
442                 result = talloc_asprintf_append(result, "DIR_NAME|");
443         if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
444                 result = talloc_asprintf_append(result, "ATTRIBUTES|");
445         if (filter & FILE_NOTIFY_CHANGE_SIZE)
446                 result = talloc_asprintf_append(result, "SIZE|");
447         if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
448                 result = talloc_asprintf_append(result, "LAST_WRITE|");
449         if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
450                 result = talloc_asprintf_append(result, "LAST_ACCESS|");
451         if (filter & FILE_NOTIFY_CHANGE_CREATION)
452                 result = talloc_asprintf_append(result, "CREATION|");
453         if (filter & FILE_NOTIFY_CHANGE_EA)
454                 result = talloc_asprintf_append(result, "EA|");
455         if (filter & FILE_NOTIFY_CHANGE_SECURITY)
456                 result = talloc_asprintf_append(result, "SECURITY|");
457         if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
458                 result = talloc_asprintf_append(result, "STREAM_NAME|");
459         if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
460                 result = talloc_asprintf_append(result, "STREAM_SIZE|");
461         if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
462                 result = talloc_asprintf_append(result, "STREAM_WRITE|");
463
464         if (result == NULL) return NULL;
465         if (*result == '\0') return result;
466
467         result[strlen(result)-1] = '\0';
468         return result;
469 }
470
471 struct sys_notify_context *sys_notify_context_create(connection_struct *conn,
472                                                      TALLOC_CTX *mem_ctx, 
473                                                      struct event_context *ev)
474 {
475         struct sys_notify_context *ctx;
476
477         if (!(ctx = TALLOC_P(mem_ctx, struct sys_notify_context))) {
478                 DEBUG(0, ("talloc failed\n"));
479                 return NULL;
480         }
481
482         ctx->ev = ev;
483         ctx->conn = conn;
484         ctx->private_data = NULL;
485         return ctx;
486 }
487
488 NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
489                           struct notify_entry *e,
490                           void (*callback)(struct sys_notify_context *ctx, 
491                                            void *private_data,
492                                            struct notify_event *ev),
493                           void *private_data, void *handle)
494 {
495         return SMB_VFS_NOTIFY_WATCH(ctx->conn, ctx, e, callback, private_data,
496                                     handle);
497 }
498