2a5d7fc5525796ff6605b46dd4a16af7faff66e8
[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
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 2 of the License, or
10    (at your option) any later version.
11
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.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 static struct cnotify_fns *cnotify;
25
26 /****************************************************************************
27  This is the structure to queue to implement NT change
28  notify. It consists of smb_size bytes stored from the
29  transact command (to keep the mid, tid etc around).
30  Plus the fid to examine and notify private data.
31 *****************************************************************************/
32
33 struct change_notify {
34         struct change_notify *next, *prev;
35         files_struct *fsp;
36         connection_struct *conn;
37         uint32 flags;
38         uint32 max_param_count;
39         char request_buf[smb_size];
40         void *change_data;
41 };
42
43 static struct change_notify *change_notify_list;
44
45 static BOOL notify_marshall_changes(struct notify_changes *changes,
46                                     prs_struct *ps)
47 {
48         int i;
49         UNISTR uni_name;
50
51         for (i=0; i<changes->num_changes; i++) {
52                 struct notify_change *c = &changes->changes[i];
53                 size_t namelen;
54                 uint32 u32_tmp; /* Temp arg to prs_uint32 to avoid
55                                  * signed/unsigned issues */
56
57                 namelen = convert_string_allocate(
58                         NULL, CH_UNIX, CH_UTF16LE, c->name, strlen(c->name)+1,
59                         &uni_name.buffer, True);
60                 if ((namelen == -1) || (uni_name.buffer == NULL)) {
61                         goto fail;
62                 }
63
64                 namelen -= 2;   /* Dump NULL termination */
65
66                 /*
67                  * Offset to next entry, only if there is one
68                  */
69
70                 u32_tmp = (i == changes->num_changes-1) ? 0 : namelen + 12;
71                 if (!prs_uint32("offset", ps, 1, &u32_tmp)) goto fail;
72
73                 u32_tmp = c->action;
74                 if (!prs_uint32("action", ps, 1, &u32_tmp)) goto fail;
75
76                 u32_tmp = namelen;
77                 if (!prs_uint32("namelen", ps, 1, &u32_tmp)) goto fail;
78
79                 if (!prs_unistr("name", ps, 1, &uni_name)) goto fail;
80
81                 /*
82                  * Not NULL terminated, decrease by the 2 UCS2 \0 chars
83                  */
84                 prs_set_offset(ps, prs_offset(ps)-2);
85
86                 SAFE_FREE(uni_name.buffer);
87         }
88
89         return True;
90
91  fail:
92         SAFE_FREE(uni_name.buffer);
93         return False;
94 }
95
96 /****************************************************************************
97  Setup the common parts of the return packet and send it.
98 *****************************************************************************/
99
100 static void change_notify_reply_packet(struct change_notify *notify,
101                                        NTSTATUS error_code)
102 {
103         char outbuf[smb_size+38];
104
105         memset(outbuf, '\0', sizeof(outbuf));
106         construct_reply_common(notify->request_buf, outbuf);
107
108         ERROR_NT(error_code);
109
110         /*
111          * Seems NT needs a transact command with an error code
112          * in it. This is a longer packet than a simple error.
113          */
114         set_message(outbuf,18,0,False);
115
116         show_msg(outbuf);
117         if (!send_smb(smbd_server_fd(),outbuf))
118                 exit_server_cleanly("change_notify_reply_packet: send_smb failed.");
119 }
120
121 static void change_notify_reply(struct change_notify *notify)
122 {
123         char *outbuf = NULL;
124         prs_struct ps;
125         size_t buflen = smb_size+38+notify->max_param_count;
126
127         if (!prs_init(&ps, 0, NULL, False)
128             || !notify_marshall_changes(notify->fsp->notify, &ps)) {
129                 change_notify_reply_packet(notify, NT_STATUS_NO_MEMORY);
130                 goto done;
131         }
132
133         if (prs_offset(&ps) > notify->max_param_count) {
134                 /*
135                  * We exceed what the client is willing to accept. Send
136                  * nothing.
137                  */
138                 change_notify_reply_packet(notify, NT_STATUS_OK);
139                 goto done;
140         }
141
142         if (!(outbuf = SMB_MALLOC_ARRAY(char, buflen))) {
143                 change_notify_reply_packet(notify, NT_STATUS_NO_MEMORY);
144                 goto done;
145         }
146
147         memset(outbuf, '\0', sizeof(outbuf));
148         construct_reply_common(notify->request_buf, outbuf);
149
150         if (send_nt_replies(outbuf, buflen, NT_STATUS_OK, prs_data_p(&ps),
151                             prs_offset(&ps), NULL, 0) == -1) {
152                 exit_server("change_notify_reply_packet: send_smb failed.");
153         }
154
155  done:
156         SAFE_FREE(outbuf);
157         prs_mem_free(&ps);
158         notify->fsp->notify->num_changes = 0;
159         TALLOC_FREE(notify->fsp->notify->changes);
160 }
161
162 /****************************************************************************
163  Remove an entry from the list and free it, also closing any
164  directory handle if necessary.
165 *****************************************************************************/
166
167 static void change_notify_remove(struct change_notify *cnbp)
168 {
169         cnotify->remove_notify(cnbp->change_data);
170         DLIST_REMOVE(change_notify_list, cnbp);
171         ZERO_STRUCTP(cnbp);
172         SAFE_FREE(cnbp);
173 }
174
175 /****************************************************************************
176  Delete entries by fnum from the change notify pending queue.
177 *****************************************************************************/
178
179 void remove_pending_change_notify_requests_by_fid(files_struct *fsp, NTSTATUS status)
180 {
181         struct change_notify *cnbp, *next;
182
183         for (cnbp=change_notify_list; cnbp; cnbp=next) {
184                 next=cnbp->next;
185                 if (cnbp->fsp->fnum == fsp->fnum) {
186                         change_notify_reply_packet(cnbp, status);
187                         change_notify_remove(cnbp);
188                 }
189         }
190 }
191
192 /****************************************************************************
193  Delete entries by mid from the change notify pending queue. Always send reply.
194 *****************************************************************************/
195
196 void remove_pending_change_notify_requests_by_mid(int mid)
197 {
198         struct change_notify *cnbp, *next;
199
200         for (cnbp=change_notify_list; cnbp; cnbp=next) {
201                 next=cnbp->next;
202                 if(SVAL(cnbp->request_buf,smb_mid) == mid) {
203                         change_notify_reply_packet(cnbp, NT_STATUS_CANCELLED);
204                         change_notify_remove(cnbp);
205                 }
206         }
207 }
208
209 /****************************************************************************
210  Delete entries by filename and cnum from the change notify pending queue.
211  Always send reply.
212 *****************************************************************************/
213
214 void remove_pending_change_notify_requests_by_filename(files_struct *fsp, NTSTATUS status)
215 {
216         struct change_notify *cnbp, *next;
217
218         for (cnbp=change_notify_list; cnbp; cnbp=next) {
219                 next=cnbp->next;
220                 /*
221                  * We know it refers to the same directory if the connection number and
222                  * the filename are identical.
223                  */
224                 if((cnbp->fsp->conn == fsp->conn) && strequal(cnbp->fsp->fsp_name,fsp->fsp_name)) {
225                         change_notify_reply_packet(cnbp, status);
226                         change_notify_remove(cnbp);
227                 }
228         }
229 }
230
231 /****************************************************************************
232  Set the current change notify timeout to the lowest value across all service
233  values.
234 ****************************************************************************/
235
236 void set_change_notify_timeout(int val)
237 {
238         if (val > 0) {
239                 cnotify->select_time = MIN(cnotify->select_time, val);
240         }
241 }
242
243 /****************************************************************************
244  Longest time to sleep for before doing a change notify scan.
245 ****************************************************************************/
246
247 int change_notify_timeout(void)
248 {
249         return cnotify->select_time;
250 }
251
252 /****************************************************************************
253  Process the change notify queue. Note that this is only called as root.
254  Returns True if there are still outstanding change notify requests on the
255  queue.
256 *****************************************************************************/
257
258 BOOL process_pending_change_notify_queue(time_t t)
259 {
260         struct change_notify *cnbp, *next;
261         uint16 vuid;
262
263         for (cnbp=change_notify_list; cnbp; cnbp=next) {
264                 next=cnbp->next;
265
266                 vuid = (lp_security() == SEC_SHARE) ? UID_FIELD_INVALID : SVAL(cnbp->request_buf,smb_uid);
267
268                 if ((cnbp->fsp->notify->num_changes != 0)
269                     || cnotify->check_notify(cnbp->conn, vuid,
270                                              cnbp->fsp->fsp_name, cnbp->flags,
271                                              cnbp->change_data, t)) {
272                         DEBUG(10,("process_pending_change_notify_queue: dir "
273                                   "%s changed !\n", cnbp->fsp->fsp_name ));
274                         change_notify_reply(cnbp);
275                         change_notify_remove(cnbp);
276                 }
277         }
278
279         return (change_notify_list != NULL);
280 }
281
282 /****************************************************************************
283  Now queue an entry on the notify change list.
284  We only need to save smb_size bytes from this incoming packet
285  as we will always by returning a 'read the directory yourself'
286  error.
287 ****************************************************************************/
288
289 BOOL change_notify_set(char *inbuf, files_struct *fsp, connection_struct *conn,
290                        uint32 flags, uint32 max_param_count)
291 {
292         struct change_notify *cnbp;
293
294         if((cnbp = SMB_MALLOC_P(struct change_notify)) == NULL) {
295                 DEBUG(0,("change_notify_set: malloc fail !\n" ));
296                 return False;
297         }
298
299         ZERO_STRUCTP(cnbp);
300
301         memcpy(cnbp->request_buf, inbuf, smb_size);
302         cnbp->fsp = fsp;
303         cnbp->conn = conn;
304         cnbp->flags = flags;
305         cnbp->max_param_count = max_param_count;
306         cnbp->change_data = cnotify->register_notify(conn, fsp->fsp_name,
307                                                      flags);
308         
309         if (!cnbp->change_data) {
310                 SAFE_FREE(cnbp);
311                 return False;
312         }
313
314         DLIST_ADD(change_notify_list, cnbp);
315
316         /* Push the MID of this packet on the signing queue. */
317         srv_defer_sign_response(SVAL(inbuf,smb_mid));
318
319         return True;
320 }
321
322 int change_notify_fd(void)
323 {
324         if (cnotify) {
325                 return cnotify->notification_fd;
326         }
327
328         return -1;
329 }
330
331 /* notify message definition
332
333 Offset  Data                    length.
334 0       SMB_DEV_T dev           8
335 8       SMB_INO_T inode         8
336 16      uint32 action           4
337 20..    name
338 */
339
340 #define MSG_NOTIFY_MESSAGE_SIZE 21 /* Includes at least the '\0' terminator */
341
342 struct notify_message {
343         SMB_DEV_T dev;
344         SMB_INO_T inode;
345         uint32_t action;
346         char *name;
347 };
348
349 static DATA_BLOB notify_message_to_buf(const struct notify_message *msg)
350 {
351         DATA_BLOB result;
352         size_t len;
353
354         len = strlen(msg->name);
355
356         result = data_blob(NULL, MSG_NOTIFY_MESSAGE_SIZE + len);
357         if (!result.data) {
358                 return result;
359         }
360
361         SDEV_T_VAL(result.data, 0, msg->dev);
362         SINO_T_VAL(result.data, 8, msg->inode);
363         SIVAL(result.data, 16, msg->action);
364         memcpy(result.data+20, msg->name, len+1);
365
366         return result;
367 }
368
369 static BOOL buf_to_notify_message(void *buf, size_t len,
370                                   struct notify_message *msg)
371 {
372         if (len < MSG_NOTIFY_MESSAGE_SIZE) {
373                 DEBUG(0, ("Got invalid notify message of len %d\n", len));
374                 return False;
375         }
376
377         msg->dev     = DEV_T_VAL(buf, 0);
378         msg->inode   = INO_T_VAL(buf, 8);
379         msg->action  = IVAL(buf, 16);
380         msg->name    = ((char *)buf)+20;
381         return True;
382 }
383
384 void notify_action(connection_struct *conn, const char *parent,
385                    const char *name, uint32_t action)
386 {
387         struct share_mode_lock *lck;
388         SMB_STRUCT_STAT sbuf;
389         int i;
390         struct notify_message msg;
391         DATA_BLOB blob;
392
393         struct process_id *pids;
394         int num_pids;
395
396         if (SMB_VFS_STAT(conn, parent, &sbuf) != 0) {
397                 /*
398                  * Not 100% critical, ignore failure
399                  */
400                 return;
401         }
402
403         if (!(lck = get_share_mode_lock(NULL, sbuf.st_dev, sbuf.st_ino,
404                                         NULL, NULL))) {
405                 return;
406         }
407
408         msg.dev = sbuf.st_dev;
409         msg.inode = sbuf.st_ino;
410         msg.action = action;
411         msg.name = CONST_DISCARD(char *, name);
412
413         blob = notify_message_to_buf(&msg);
414         if (blob.data == NULL) {
415                 DEBUG(0, ("notify_message_to_buf failed\n"));
416                 return;
417         }
418
419         pids = NULL;
420         num_pids = 0;
421
422         become_root_uid_only();
423
424         for (i=0; i<lck->num_share_modes; i++) {
425                 struct share_mode_entry *e = &lck->share_modes[i];
426                 int j;
427                 struct process_id *tmp;
428
429                 for (j=0; j<num_pids; j++) {
430                         if (procid_equal(&e->pid, &pids[j])) {
431                                 break;
432                         }
433                 }
434
435                 if (j < num_pids) {
436                         /*
437                          * Already sent to that process, skip it
438                          */
439                         continue;
440                 }
441
442                 message_send_pid(lck->share_modes[i].pid, MSG_SMB_NOTIFY,
443                                  blob.data, blob.length, True);
444
445                 if (!(tmp = TALLOC_REALLOC_ARRAY(lck, pids, struct process_id,
446                                                  num_pids+1))) {
447                         DEBUG(0, ("realloc failed\n"));
448                         break;
449                 }
450                 pids = tmp;
451                 pids[num_pids] = e->pid;
452                 num_pids += 1;
453         }
454
455         unbecome_root_uid_only();
456
457         data_blob_free(&blob);
458         TALLOC_FREE(lck);
459 }
460
461 static void notify_message(int msgtype, struct process_id pid,
462                            void *buf, size_t len)
463 {
464         struct notify_message msg;
465         files_struct *fsp;
466         struct notify_change *changes, *change;
467
468         if (!buf_to_notify_message(buf, len, &msg)) {
469                 return;
470         }
471
472         DEBUG(10, ("Received notify_message for 0x%x/%.0f: %d\n",
473                    (unsigned)msg.dev, (double)msg.inode, msg.action));
474
475         if (!(fsp = file_find_dir_lowest_id(msg.dev, msg.inode))) {
476                 DEBUG(10, ("notify_message: did not find fsp\n"));
477                 return;
478         }
479
480         if (!(changes = TALLOC_REALLOC_ARRAY(
481                       fsp->notify, fsp->notify->changes,
482                       struct notify_change, fsp->notify->num_changes+1))) {
483                 DEBUG(0, ("talloc_realloc failed\n"));
484                 return;
485         }
486
487         fsp->notify->changes = changes;
488
489         change = &(fsp->notify->changes[fsp->notify->num_changes]);
490
491         if (!(change->name = talloc_strdup(changes, msg.name))) {
492                 DEBUG(0, ("talloc_strdup failed\n"));
493                 return;
494         }
495         change->action = msg.action;
496         fsp->notify->num_changes += 1;
497 }
498
499 /****************************************************************************
500  Initialise the change notify subsystem.
501 ****************************************************************************/
502
503 BOOL init_change_notify(void)
504 {
505         cnotify = NULL;
506
507 #if HAVE_KERNEL_CHANGE_NOTIFY
508         if (cnotify == NULL && lp_kernel_change_notify())
509                 cnotify = kernel_notify_init();
510 #endif
511 #if HAVE_FAM_CHANGE_NOTIFY
512         if (cnotify == NULL && lp_fam_change_notify())
513                 cnotify = fam_notify_init();
514 #endif
515         if (!cnotify) cnotify = hash_notify_init();
516         
517         if (!cnotify) {
518                 DEBUG(0,("Failed to init change notify system\n"));
519                 return False;
520         }
521
522         message_register(MSG_SMB_NOTIFY, notify_message);
523
524         return True;
525 }