r21092: Ok, that's the one that activates the Samba4 notify backend.
[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, BOOL recursive,
163                                    struct files_struct *fsp)
164 {
165         struct notify_change_request *request = NULL;
166         struct notify_mid_map *map = NULL;
167
168         if (!(request = SMB_MALLOC_P(struct notify_change_request))
169             || !(map = SMB_MALLOC_P(struct notify_mid_map))) {
170                 SAFE_FREE(request);
171                 return NT_STATUS_NO_MEMORY;
172         }
173
174         request->mid_map = map;
175         map->req = request;
176
177         memcpy(request->request_buf, inbuf, sizeof(request->request_buf));
178         request->max_param_count = max_param_count;
179         request->filter = filter;
180         request->fsp = fsp;
181         request->backend_data = NULL;
182         
183         DLIST_ADD_END(fsp->notify->requests, request,
184                       struct notify_change_request *);
185
186         map->mid = SVAL(inbuf, smb_mid);
187         DLIST_ADD(notify_changes_by_mid, map);
188
189         /* Push the MID of this packet on the signing queue. */
190         srv_defer_sign_response(SVAL(inbuf,smb_mid));
191
192         return NT_STATUS_OK;
193 }
194
195 static void change_notify_remove_request(struct notify_change_request *remove_req)
196 {
197         files_struct *fsp;
198         struct notify_change_request *req;
199
200         /*
201          * Paranoia checks, the fsp referenced must must have the request in
202          * its list of pending requests
203          */
204
205         fsp = remove_req->fsp;
206         SMB_ASSERT(fsp->notify != NULL);
207
208         for (req = fsp->notify->requests; req; req = req->next) {
209                 if (req == remove_req) {
210                         break;
211                 }
212         }
213
214         if (req == NULL) {
215                 smb_panic("notify_req not found in fsp's requests\n");
216         }
217
218         DLIST_REMOVE(fsp->notify->requests, req);
219         DLIST_REMOVE(notify_changes_by_mid, req->mid_map);
220         SAFE_FREE(req->mid_map);
221         TALLOC_FREE(req->backend_data);
222         SAFE_FREE(req);
223 }
224
225 /****************************************************************************
226  Delete entries by mid from the change notify pending queue. Always send reply.
227 *****************************************************************************/
228
229 void remove_pending_change_notify_requests_by_mid(uint16 mid)
230 {
231         struct notify_mid_map *map;
232
233         for (map = notify_changes_by_mid; map; map = map->next) {
234                 if (map->mid == mid) {
235                         break;
236                 }
237         }
238
239         if (map == NULL) {
240                 return;
241         }
242
243         change_notify_reply_packet(map->req->request_buf, NT_STATUS_CANCELLED);
244         change_notify_remove_request(map->req);
245 }
246
247 /****************************************************************************
248  Delete entries by fnum from the change notify pending queue.
249 *****************************************************************************/
250
251 void remove_pending_change_notify_requests_by_fid(files_struct *fsp,
252                                                   NTSTATUS status)
253 {
254         if (fsp->notify == NULL) {
255                 return;
256         }
257
258         while (fsp->notify->requests != NULL) {
259                 change_notify_reply_packet(
260                         fsp->notify->requests->request_buf, status);
261                 change_notify_remove_request(fsp->notify->requests);
262         }
263 }
264
265 /* notify message definition
266
267 Offset  Data                    length.
268 0       SMB_DEV_T dev           8
269 8       SMB_INO_T inode         8
270 16      uint32 filter           4
271 20      uint32 action           4
272 24..    name
273 */
274
275 #define MSG_NOTIFY_MESSAGE_SIZE 25 /* Includes at least the '\0' terminator */
276
277 struct notify_message {
278         SMB_DEV_T dev;
279         SMB_INO_T inode;
280         uint32 filter;
281         uint32 action;
282         char *name;
283 };
284
285 static DATA_BLOB notify_message_to_buf(const struct notify_message *msg)
286 {
287         DATA_BLOB result;
288         size_t len;
289
290         len = strlen(msg->name);
291
292         result = data_blob(NULL, MSG_NOTIFY_MESSAGE_SIZE + len);
293         if (!result.data) {
294                 return result;
295         }
296
297         SDEV_T_VAL(result.data, 0, msg->dev);
298         SINO_T_VAL(result.data, 8, msg->inode);
299         SIVAL(result.data, 16, msg->filter);
300         SIVAL(result.data, 20, msg->action);
301         memcpy(result.data+24, msg->name, len+1);
302
303         return result;
304 }
305
306 static BOOL buf_to_notify_message(void *buf, size_t len,
307                                   struct notify_message *msg)
308 {
309         if (len < MSG_NOTIFY_MESSAGE_SIZE) {
310                 DEBUG(0, ("Got invalid notify message of len %d\n",
311                           (int)len));
312                 return False;
313         }
314
315         msg->dev     = DEV_T_VAL(buf, 0);
316         msg->inode   = INO_T_VAL(buf, 8);
317         msg->filter  = IVAL(buf, 16);
318         msg->action  = IVAL(buf, 20);
319         msg->name    = ((char *)buf)+24;
320         return True;
321 }
322
323 void notify_action(connection_struct *conn, const char *parent,
324                    const char *name, uint32 filter, uint32_t action)
325 {
326         struct share_mode_lock *lck;
327         SMB_STRUCT_STAT sbuf;
328         int i;
329         struct notify_message msg;
330         DATA_BLOB blob;
331
332         struct process_id *pids;
333         int num_pids;
334
335         DEBUG(10, ("notify_action: parent=%s, name=%s, action=%u\n",
336                    parent, name, (unsigned)action));
337
338         if (SMB_VFS_STAT(conn, parent, &sbuf) != 0) {
339                 /*
340                  * Not 100% critical, ignore failure
341                  */
342                 return;
343         }
344
345         {
346                 char *fullpath;
347
348                 if (asprintf(&fullpath, "%s/%s/%s", conn->connectpath,
349                              parent, name) != -1) {
350                         notify_trigger(conn->notify_ctx, action, filter,
351                                        fullpath);
352                         SAFE_FREE(fullpath);
353                 }
354                 return;
355         }
356
357         if (!(lck = get_share_mode_lock(NULL, sbuf.st_dev, sbuf.st_ino,
358                                         NULL, NULL))) {
359                 return;
360         }
361
362         msg.dev = sbuf.st_dev;
363         msg.inode = sbuf.st_ino;
364         msg.filter = filter;
365         msg.action = action;
366         msg.name = CONST_DISCARD(char *, name);
367
368         blob = notify_message_to_buf(&msg);
369         if (blob.data == NULL) {
370                 DEBUG(0, ("notify_message_to_buf failed\n"));
371                 return;
372         }
373
374         pids = NULL;
375         num_pids = 0;
376
377         become_root_uid_only();
378
379         for (i=0; i<lck->num_share_modes; i++) {
380                 struct share_mode_entry *e = &lck->share_modes[i];
381                 int j;
382                 struct process_id *tmp;
383
384                 for (j=0; j<num_pids; j++) {
385                         if (procid_equal(&e->pid, &pids[j])) {
386                                 break;
387                         }
388                 }
389
390                 if (j < num_pids) {
391                         /*
392                          * Already sent to that process, skip it
393                          */
394                         continue;
395                 }
396
397                 message_send_pid(lck->share_modes[i].pid, MSG_SMB_NOTIFY,
398                                  blob.data, blob.length, True);
399
400                 if (!(tmp = TALLOC_REALLOC_ARRAY(lck, pids, struct process_id,
401                                                  num_pids+1))) {
402                         DEBUG(0, ("realloc failed\n"));
403                         break;
404                 }
405                 pids = tmp;
406                 pids[num_pids] = e->pid;
407                 num_pids += 1;
408         }
409
410         unbecome_root_uid_only();
411
412         data_blob_free(&blob);
413         TALLOC_FREE(lck);
414 }
415
416 void notify_fname(connection_struct *conn, uint32 action, uint32 filter,
417                   const char *path)
418 {
419         char *fullpath;
420
421         if (asprintf(&fullpath, "%s/%s", conn->connectpath, path) == -1) {
422                 DEBUG(0, ("asprintf failed\n"));
423                 return;
424         }
425
426         notify_trigger(conn->notify_ctx, action, filter, fullpath);
427         SAFE_FREE(fullpath);
428 }
429
430 void notify_fsp(files_struct *fsp, uint32 action, const char *name)
431 {
432         struct notify_change *change, *changes;
433         char *name2;
434
435         if (fsp->notify == NULL) {
436                 /*
437                  * Nobody is waiting, don't queue
438                  */
439                 return;
440         }
441
442         if (!(name2 = talloc_strdup(fsp->notify, name))) {
443                 DEBUG(0, ("talloc_strdup failed\n"));
444                         return;
445                 }
446
447         string_replace(name2, '/', '\\');
448
449         /*
450          * Someone has triggered a notify previously, queue the change for
451          * later. TODO: Limit the number of changes queued, test how filters
452          * apply here. Do we have to store them?
453          */
454
455         if ((fsp->notify->num_changes > 30) || (name == NULL)) {
456                 /*
457                  * W2k3 seems to store at most 30 changes.
458                  */
459                 TALLOC_FREE(fsp->notify->changes);
460                 TALLOC_FREE(name2);
461                 fsp->notify->num_changes = -1;
462                 return;
463         }
464
465         if (fsp->notify->num_changes == -1) {
466                 return;
467         }
468
469         if (!(changes = TALLOC_REALLOC_ARRAY(
470                       fsp->notify, fsp->notify->changes,
471                       struct notify_change, fsp->notify->num_changes+1))) {
472                 DEBUG(0, ("talloc_realloc failed\n"));
473                 TALLOC_FREE(name2);
474                 return;
475         }
476
477         fsp->notify->changes = changes;
478
479         change = &(fsp->notify->changes[fsp->notify->num_changes]);
480
481         change->name = talloc_move(changes, &name2);
482         change->action = action;
483         fsp->notify->num_changes += 1;
484
485         if (fsp->notify->requests == NULL) {
486                 /*
487                  * Nobody is waiting, so don't send anything. The ot
488                  */
489                 return;
490         }
491
492         if (action == NOTIFY_ACTION_OLD_NAME) {
493                 /*
494                  * We have to send the two rename events in one reply. So hold
495                  * the first part back.
496                  */
497         return;
498         }
499
500         /*
501          * Someone is waiting for the change, trigger the reply immediately.
502          *
503          * TODO: do we have to walk the lists of requests pending?
504          */
505
506         change_notify_reply(fsp->notify->requests->request_buf,
507                             fsp->notify->requests->max_param_count,
508                             fsp->notify->num_changes,
509                             fsp->notify->changes);
510
511         change_notify_remove_request(fsp->notify->requests);
512
513         TALLOC_FREE(fsp->notify->changes);
514         fsp->notify->num_changes = 0;
515 }
516
517 static void notify_message_callback(int msgtype, struct process_id pid,
518                                     void *buf, size_t len,
519                                     void *private_data)
520 {
521         struct notify_message msg;
522         files_struct *fsp;
523
524         if (!buf_to_notify_message(buf, len, &msg)) {
525                 return;
526         }
527
528         DEBUG(10, ("Received notify_message for 0x%x/%.0f: %d\n",
529                    (unsigned)msg.dev, (double)msg.inode, msg.action));
530
531         for(fsp = fsp_find_di_first(msg.dev, msg.inode); fsp;
532             fsp = fsp_find_di_next(fsp)) {
533                 if ((fsp->notify != NULL) 
534                     && (fsp->notify->requests != NULL)
535                     && (fsp->notify->requests->filter & msg.filter)) {
536                         notify_fsp(fsp, msg.action, msg.name);
537                 }
538         }
539 }
540
541 char *notify_filter_string(TALLOC_CTX *mem_ctx, uint32 filter)
542 {
543         char *result = NULL;
544
545         result = talloc_strdup(mem_ctx, "");
546
547         if (filter & FILE_NOTIFY_CHANGE_FILE_NAME)
548                 result = talloc_asprintf_append(result, "FILE_NAME|");
549         if (filter & FILE_NOTIFY_CHANGE_DIR_NAME)
550                 result = talloc_asprintf_append(result, "DIR_NAME|");
551         if (filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)
552                 result = talloc_asprintf_append(result, "ATTRIBUTES|");
553         if (filter & FILE_NOTIFY_CHANGE_SIZE)
554                 result = talloc_asprintf_append(result, "SIZE|");
555         if (filter & FILE_NOTIFY_CHANGE_LAST_WRITE)
556                 result = talloc_asprintf_append(result, "LAST_WRITE|");
557         if (filter & FILE_NOTIFY_CHANGE_LAST_ACCESS)
558                 result = talloc_asprintf_append(result, "LAST_ACCESS|");
559         if (filter & FILE_NOTIFY_CHANGE_CREATION)
560                 result = talloc_asprintf_append(result, "CREATION|");
561         if (filter & FILE_NOTIFY_CHANGE_EA)
562                 result = talloc_asprintf_append(result, "EA|");
563         if (filter & FILE_NOTIFY_CHANGE_SECURITY)
564                 result = talloc_asprintf_append(result, "SECURITY|");
565         if (filter & FILE_NOTIFY_CHANGE_STREAM_NAME)
566                 result = talloc_asprintf_append(result, "STREAM_NAME|");
567         if (filter & FILE_NOTIFY_CHANGE_STREAM_SIZE)
568                 result = talloc_asprintf_append(result, "STREAM_SIZE|");
569         if (filter & FILE_NOTIFY_CHANGE_STREAM_WRITE)
570                 result = talloc_asprintf_append(result, "STREAM_WRITE|");
571
572         if (result == NULL) return NULL;
573         if (*result == '\0') return result;
574
575         result[strlen(result)-1] = '\0';
576         return result;
577 }
578
579 /****************************************************************************
580  Initialise the change notify subsystem.
581 ****************************************************************************/
582
583 BOOL init_change_notify(void)
584 {
585         cnotify = NULL;
586
587 #if HAVE_KERNEL_CHANGE_NOTIFY
588         if (cnotify == NULL && lp_kernel_change_notify())
589                 cnotify = kernel_notify_init(smbd_event_context());
590 #endif
591 #if HAVE_FAM_CHANGE_NOTIFY
592         if (cnotify == NULL && lp_fam_change_notify())
593                 cnotify = fam_notify_init(smbd_event_context());
594 #endif
595         if (!cnotify) cnotify = hash_notify_init();
596         
597         if (!cnotify) {
598                 DEBUG(0,("Failed to init change notify system\n"));
599                 return False;
600         }
601
602         message_register(MSG_SMB_NOTIFY, notify_message_callback, NULL);
603
604         return True;
605 }
606
607 struct sys_notify_context *sys_notify_context_create(struct share_params *scfg,
608                                                      TALLOC_CTX *mem_ctx, 
609                                                      struct event_context *ev)
610 {
611         struct sys_notify_context *ctx;
612
613         if (!(ctx = TALLOC_P(mem_ctx, struct sys_notify_context))) {
614                 DEBUG(0, ("talloc failed\n"));
615                 return NULL;
616         }
617
618         ctx->ev = ev;
619         ctx->private_data = NULL;
620         return ctx;
621 }
622
623 NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
624                           struct notify_entry *e,
625                           void (*callback)(struct sys_notify_context *ctx, 
626                                            void *private_data,
627                                            struct notify_event *ev),
628                           void *private_data, void *handle)
629 {
630 #ifdef HAVE_INOTIFY
631         return inotify_watch(ctx, e, callback, private_data, handle);
632 #else
633         return NT_STATUS_OK;
634 #endif
635 }
636