97192d6e63c34b13d257615ebb2618b8c5b81850
[tprouty/samba.git] / source / 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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 static struct cnotify_fns *cnotify;
26 static struct notify_mid_map *notify_changes_by_mid;
27
28 /*
29  * For NTCancel, we need to find the notify_change_request indexed by
30  * mid. Separate list here.
31  */
32
33 struct notify_mid_map {
34         struct notify_mid_map *prev, *next;
35         struct notify_change_request *req;
36         uint16 mid;
37 };
38
39 static BOOL notify_marshall_changes(int num_changes,
40                                     struct notify_change *changes,
41                                     prs_struct *ps)
42 {
43         int i;
44         UNISTR uni_name;
45
46         for (i=0; i<num_changes; i++) {
47                 struct notify_change *c = &changes[i];
48                 size_t namelen;
49                 uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid
50                                  * signed/unsigned issues */
51
52                 namelen = convert_string_allocate(
53                         NULL, CH_UNIX, CH_UTF16LE, c->name, strlen(c->name)+1,
54                         &uni_name.buffer, True);
55                 if ((namelen == -1) || (uni_name.buffer == NULL)) {
56                         goto fail;
57                 }
58
59                 namelen -= 2;   /* Dump NULL termination */
60
61                 /*
62                  * Offset to next entry, only if there is one
63                  */
64
65                 u32_tmp = (i == num_changes-1) ? 0 : namelen + 12;
66                 if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail;
67
68                 u32_tmp = c->action;
69                 if (!prs_uint32("action", ps, 1, &u32_tmp)) goto fail;
70
71                 u32_tmp = namelen;
72                 if (!prs_uint32("namelen", ps, 1, &u32_tmp)) goto fail;
73
74                 if (!prs_unistr("name", ps, 1, &uni_name)) goto fail;
75
76                 /*
77                  * Not NULL terminated, decrease by the 2 UCS2 \0 chars
78                  */
79                 prs_set_offset(ps, prs_offset(ps)-2);
80
81                 SAFE_FREE(uni_name.buffer);
82         }
83
84         return True;
85
86  fail:
87         SAFE_FREE(uni_name.buffer);
88         return False;
89 }
90
91 /****************************************************************************
92  Setup the common parts of the return packet and send it.
93 *****************************************************************************/
94
95 static void change_notify_reply_packet(const char *request_buf,
96                                        NTSTATUS error_code)
97 {
98         char outbuf[smb_size+38];
99
100         memset(outbuf, '\0', sizeof(outbuf));
101         construct_reply_common(request_buf, outbuf);
102
103         ERROR_NT(error_code);
104
105         /*
106          * Seems NT needs a transact command with an error code
107          * in it. This is a longer packet than a simple error.
108          */
109         set_message(outbuf,18,0,False);
110
111         show_msg(outbuf);
112         if (!send_smb(smbd_server_fd(),outbuf))
113                 exit_server_cleanly("change_notify_reply_packet: send_smb "
114                                     "failed.");
115 }
116
117 void change_notify_reply(const char *request_buf, uint32 max_param_count,
118                          int num_changes, struct notify_change *changes)
119 {
120         char *outbuf = NULL;
121         prs_struct ps;
122         size_t buflen = smb_size+38+max_param_count;
123
124         if (num_changes == -1) {
125                 change_notify_reply_packet(request_buf, NT_STATUS_OK);
126                 return;
127         }
128
129         if (!prs_init(&ps, 0, NULL, False)
130             || !notify_marshall_changes(num_changes, changes, &ps)) {
131                 change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY);
132                 goto done;
133         }
134
135         if (prs_offset(&ps) > max_param_count) {
136                 /*
137                  * We exceed what the client is willing to accept. Send
138                  * nothing.
139                  */
140                 change_notify_reply_packet(request_buf, NT_STATUS_OK);
141                 goto done;
142         }
143
144         if (!(outbuf = SMB_MALLOC_ARRAY(char, buflen))) {
145                 change_notify_reply_packet(request_buf, NT_STATUS_NO_MEMORY);
146                 goto done;
147         }
148
149         construct_reply_common(request_buf, outbuf);
150
151         if (send_nt_replies(outbuf, buflen, NT_STATUS_OK, prs_data_p(&ps),
152                             prs_offset(&ps), NULL, 0) == -1) {
153                 exit_server("change_notify_reply_packet: send_smb failed.");
154         }
155
156  done:
157         SAFE_FREE(outbuf);
158         prs_mem_free(&ps);
159 }
160
161 NTSTATUS change_notify_add_request(const char *inbuf, uint32 max_param_count,
162                                    uint32 filter, struct files_struct *fsp)
163 {
164         struct notify_change_request *request = NULL;
165         struct notify_mid_map *map = NULL;
166
167         if (!(request = SMB_MALLOC_P(struct notify_change_request))
168             || !(map = SMB_MALLOC_P(struct notify_mid_map))) {
169                 SAFE_FREE(request);
170                 return NT_STATUS_NO_MEMORY;
171         }
172
173         request->mid_map = map;
174         map->req = request;
175
176         memcpy(request->request_buf, inbuf, sizeof(request->request_buf));
177         request->max_param_count = max_param_count;
178         request->filter = filter;
179         request->fsp = fsp;
180
181         request->backend_data = cnotify->notify_add(NULL, smbd_event_context(),
182                                                     fsp, &request->filter);
183         
184         DLIST_ADD_END(fsp->notify->requests, request,
185                       struct notify_change_request *);
186
187         map->mid = SVAL(inbuf, smb_mid);
188         DLIST_ADD(notify_changes_by_mid, map);
189
190         /* Push the MID of this packet on the signing queue. */
191         srv_defer_sign_response(SVAL(inbuf,smb_mid));
192
193         return NT_STATUS_OK;
194 }
195
196 static void change_notify_remove_request(struct notify_change_request *remove_req)
197 {
198         files_struct *fsp;
199         struct notify_change_request *req;
200
201         /*
202          * Paranoia checks, the fsp referenced must must have the request in
203          * its list of pending requests
204          */
205
206         fsp = remove_req->fsp;
207         SMB_ASSERT(fsp->notify != NULL);
208
209         for (req = fsp->notify->requests; req; req = req->next) {
210                 if (req == remove_req) {
211                         break;
212                 }
213         }
214
215         if (req == NULL) {
216                 smb_panic("notify_req not found in fsp's requests\n");
217         }
218
219         DLIST_REMOVE(fsp->notify->requests, req);
220         DLIST_REMOVE(notify_changes_by_mid, req->mid_map);
221         SAFE_FREE(req->mid_map);
222         TALLOC_FREE(req->backend_data);
223         SAFE_FREE(req);
224 }
225
226 /****************************************************************************
227  Delete entries by mid from the change notify pending queue. Always send reply.
228 *****************************************************************************/
229
230 void remove_pending_change_notify_requests_by_mid(uint16 mid)
231 {
232         struct notify_mid_map *map;
233
234         for (map = notify_changes_by_mid; map; map = map->next) {
235                 if (map->mid == mid) {
236                         break;
237                 }
238         }
239
240         if (map == NULL) {
241                 return;
242         }
243
244         change_notify_reply_packet(map->req->request_buf, NT_STATUS_CANCELLED);
245         change_notify_remove_request(map->req);
246 }
247
248 /****************************************************************************
249  Delete entries by fnum from the change notify pending queue.
250 *****************************************************************************/
251
252 void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
253                                                   NTSTATUS status)
254 {
255         if (fsp->notify == NULL) {
256                 return;
257         }
258
259         while (fsp->notify->requests != NULL) {
260                 change_notify_reply_packet(
261                         fsp->notify->requests->request_buf, status);
262                 change_notify_remove_request(fsp->notify->requests);
263         }
264 }
265
266 /* notify message definition
267
268 Offset  Data                    length.
269 0       SMB_DEV_T dev           8
270 8       SMB_INO_T inode         8
271 16      uint32 filter           4
272 20      uint32 action           4
273 24..    name
274 */
275
276 #define MSG_NOTIFY_MESSAGE_SIZE 25 /* Includes at least the '\0' terminator */
277
278 struct notify_message {
279         SMB_DEV_T dev;
280         SMB_INO_T inode;
281         uint32 filter;
282         uint32 action;
283         char *name;
284 };
285
286 static DATA_BLOB notify_message_to_buf(const struct notify_message *msg)
287 {
288         DATA_BLOB result;
289         size_t len;
290
291         len = strlen(msg->name);
292
293         result = data_blob(NULL, MSG_NOTIFY_MESSAGE_SIZE + len);
294         if (!result.data) {
295                 return result;
296         }
297
298         SDEV_T_VAL(result.data, 0, msg->dev);
299         SINO_T_VAL(result.data, 8, msg->inode);
300         SIVAL(result.data, 16, msg->filter);
301         SIVAL(result.data, 20, msg->action);
302         memcpy(result.data+24, msg->name, len+1);
303
304         return result;
305 }
306
307 static BOOL buf_to_notify_message(void *buf, size_t len,
308                                   struct notify_message *msg)
309 {
310         if (len < MSG_NOTIFY_MESSAGE_SIZE) {
311                 DEBUG(0, ("Got invalid notify message of len %d\n",
312                           (int)len));
313                 return False;
314         }
315
316         msg->dev     = DEV_T_VAL(buf, 0);
317         msg->inode   = INO_T_VAL(buf, 8);
318         msg->filter  = IVAL(buf, 16);
319         msg->action  = IVAL(buf, 20);
320         msg->name    = ((char *)buf)+24;
321         return True;
322 }
323
324 void notify_action(connection_struct *conn, const char *parent,
325                    const char *name, uint32 filter, uint32_t action)
326 {
327         struct share_mode_lock *lck;
328         SMB_STRUCT_STAT sbuf;
329         int i;
330         struct notify_message msg;
331         DATA_BLOB blob;
332
333         struct process_id *pids;
334         int num_pids;
335
336         DEBUG(10, ("notify_action: parent=%s, name=%s, action=%u\n",
337                    parent, name, (unsigned)action));
338
339         if (SMB_VFS_STAT(conn, parent, &sbuf) != 0) {
340                 /*
341                  * Not 100% critical, ignore failure
342                  */
343                 return;
344         }
345
346         if (!(lck = get_share_mode_lock(NULL, sbuf.st_dev, sbuf.st_ino,
347                                         NULL, NULL))) {
348                 return;
349         }
350
351         msg.dev = sbuf.st_dev;
352         msg.inode = sbuf.st_ino;
353         msg.filter = filter;
354         msg.action = action;
355         msg.name = CONST_DISCARD(char *, name);
356
357         blob = notify_message_to_buf(&msg);
358         if (blob.data == NULL) {
359                 DEBUG(0, ("notify_message_to_buf failed\n"));
360                 return;
361         }
362
363         pids = NULL;
364         num_pids = 0;
365
366         become_root_uid_only();
367
368         for (i=0; i<lck->num_share_modes; i++) {
369                 struct share_mode_entry *e = &lck->share_modes[i];
370                 int j;
371                 struct process_id *tmp;
372
373                 for (j=0; j<num_pids; j++) {
374                         if (procid_equal(&e->pid, &pids[j])) {
375                                 break;
376                         }
377                 }
378
379                 if (j < num_pids) {
380                         /*
381                          * Already sent to that process, skip it
382                          */
383                         continue;
384                 }
385
386                 message_send_pid(lck->share_modes[i].pid, MSG_SMB_NOTIFY,
387                                  blob.data, blob.length, True);
388
389                 if (!(tmp = TALLOC_REALLOC_ARRAY(lck, pids, struct process_id,
390                                                  num_pids+1))) {
391                         DEBUG(0, ("realloc failed\n"));
392                         break;
393                 }
394                 pids = tmp;
395                 pids[num_pids] = e->pid;
396                 num_pids += 1;
397         }
398
399         unbecome_root_uid_only();
400
401         data_blob_free(&blob);
402         TALLOC_FREE(lck);
403 }
404
405 void notify_fname(connection_struct *conn, const char *path,
406                   uint32 filter, uint32 action)
407 {
408         char *parent;
409         const char *name;
410
411         if (!parent_dirname_talloc(tmp_talloc_ctx(), path, &parent, &name)) {
412                 return;
413         }
414
415         notify_action(conn, parent, name, filter, action);
416         TALLOC_FREE(parent);
417 }
418
419 void notify_fsp(files_struct *fsp, uint32 action, char *name)
420 {
421         struct notify_change *change, *changes;
422
423         if (fsp->notify == NULL) {
424                 /*
425                  * Nobody is waiting, don't queue
426                  */
427                 return;
428         }
429
430         if (fsp->notify->requests != NULL) {
431                 /*
432                  * Someone is waiting for the change, trigger the reply
433                  * immediately.
434                  *
435                  * TODO: do we have to walk the lists of requests pending?
436                  */
437
438                 struct notify_change_request *req = fsp->notify->requests;
439                 struct notify_change onechange;
440
441                 if (name == NULL) {
442                         /*
443                          * Catch-all change, possibly from notify_hash.c
444                          */
445                         change_notify_reply(req->request_buf,
446                                             req->max_param_count,
447                                             -1, NULL);
448                         return;
449                 }
450
451                 onechange.action = action;
452                 onechange.name = name;
453
454                 change_notify_reply(req->request_buf, req->max_param_count,
455                                     1, &onechange);
456                 change_notify_remove_request(req);
457                 return;
458         }
459
460         /*
461          * Someone has triggered a notify previously, queue the change for
462          * later. TODO: Limit the number of changes queued, test how filters
463          * apply here. Do we have to store them?
464          */
465
466         if ((fsp->notify->num_changes > 30) || (name == NULL)) {
467                 /*
468                  * W2k3 seems to store at most 30 changes.
469                  */
470                 TALLOC_FREE(fsp->notify->changes);
471                 fsp->notify->num_changes = -1;
472                 return;
473         }
474
475         if (fsp->notify->num_changes == -1) {
476                 return;
477         }
478
479         if (!(changes = TALLOC_REALLOC_ARRAY(
480                       fsp->notify, fsp->notify->changes,
481                       struct notify_change, fsp->notify->num_changes+1))) {
482                 DEBUG(0, ("talloc_realloc failed\n"));
483                 return;
484         }
485
486         fsp->notify->changes = changes;
487
488         change = &(fsp->notify->changes[fsp->notify->num_changes]);
489
490         if (!(change->name = talloc_strdup(changes, name))) {
491                 DEBUG(0, ("talloc_strdup failed\n"));
492                 return;
493         }
494         change->action = action;
495         fsp->notify->num_changes += 1;
496
497         return;
498 }
499
500 static void notify_message_callback(int msgtype, struct process_id pid,
501                                     void *buf, size_t len,
502                                     void *private_data)
503 {
504         struct notify_message msg;
505         files_struct *fsp;
506
507         if (!buf_to_notify_message(buf, len, &msg)) {
508                 return;
509         }
510
511         DEBUG(10, ("Received notify_message for 0x%x/%.0f: %d\n",
512                    (unsigned)msg.dev, (double)msg.inode, msg.action));
513
514         for(fsp = fsp_find_di_first(msg.dev, msg.inode); fsp;
515             fsp = fsp_find_di_next(fsp)) {
516                 if ((fsp->notify != NULL) 
517                     && (fsp->notify->requests != NULL)
518                     && (fsp->notify->requests->filter & msg.filter)) {
519                         notify_fsp(fsp, msg.action, msg.name);
520                 }
521         }
522 }
523
524 /****************************************************************************
525  Initialise the change notify subsystem.
526 ****************************************************************************/
527
528 BOOL init_change_notify(void)
529 {
530         cnotify = NULL;
531
532 #if HAVE_KERNEL_CHANGE_NOTIFY
533         if (cnotify == NULL && lp_kernel_change_notify())
534                 cnotify = kernel_notify_init(smbd_event_context());
535 #endif
536 #if HAVE_FAM_CHANGE_NOTIFY
537         if (cnotify == NULL && lp_fam_change_notify())
538                 cnotify = fam_notify_init(smbd_event_context());
539 #endif
540         if (!cnotify) cnotify = hash_notify_init();
541         
542         if (!cnotify) {
543                 DEBUG(0,("Failed to init change notify system\n"));
544                 return False;
545         }
546
547         message_register(MSG_SMB_NOTIFY, notify_message_callback, NULL);
548
549         return True;
550 }
551
552 struct sys_notify_context *sys_notify_context_create(struct share_params *scfg,
553                                                      TALLOC_CTX *mem_ctx, 
554                                                      struct event_context *ev)
555 {
556         struct sys_notify_context *ctx;
557
558         if (!(ctx = TALLOC_P(mem_ctx, struct sys_notify_context))) {
559                 DEBUG(0, ("talloc failed\n"));
560                 return NULL;
561         }
562
563         ctx->ev = ev;
564         ctx->private_data = NULL;
565         return ctx;
566 }
567
568 NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
569                           struct notify_entry *e,
570                           void (*callback)(struct sys_notify_context *ctx, 
571                                            void *private_data,
572                                            struct notify_event *ev),
573                           void *private_data, void *handle)
574 {
575         return inotify_watch(ctx, e, callback, private_data, handle);
576 }
577