f90414dc798ad3c0df0de5ff089a266490888100
[nivanova/samba-autobuild/.git] / source3 / smbd / notify_kernel.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    change notify handling - linux kernel based implementation
5    Copyright (C) Andrew Tridgell 2000
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 #if HAVE_KERNEL_CHANGE_NOTIFY
26
27 #ifndef DN_ACCESS
28 #define DN_ACCESS       0x00000001      /* File accessed in directory */
29 #define DN_MODIFY       0x00000002      /* File modified in directory */
30 #define DN_CREATE       0x00000004      /* File created in directory */
31 #define DN_DELETE       0x00000008      /* File removed from directory */
32 #define DN_RENAME       0x00000010      /* File renamed in directory */
33 #define DN_ATTRIB       0x00000020      /* File changed attribute */
34 #define DN_MULTISHOT    0x80000000      /* Don't remove notifier */
35 #endif
36
37
38 #ifndef RT_SIGNAL_NOTIFY
39 #define RT_SIGNAL_NOTIFY (SIGRTMIN+2)
40 #endif
41
42 #ifndef F_SETSIG
43 #define F_SETSIG 10
44 #endif
45
46 #ifndef F_NOTIFY
47 #define F_NOTIFY 1026
48 #endif
49
50 /****************************************************************************
51  This is the structure to keep the information needed to
52  determine if a directory has changed.
53 *****************************************************************************/
54
55 struct dnotify_ctx {
56         struct dnotify_ctx *prev, *next;
57
58         int fd;
59         files_struct *fsp;
60 };
61
62 static struct dnotify_ctx *dnotify_list;
63 static int dnotify_signal_pipe[2];
64
65 /****************************************************************************
66  The signal handler for change notify.
67  The Linux kernel has a bug in that we should be able to block any
68  further delivery of RT signals until the kernel_check_notify() function
69  unblocks them, but it seems that any signal mask we're setting here is
70  being overwritten on exit from this handler. I should create a standalone
71  test case for the kernel hackers. JRA.
72 *****************************************************************************/
73
74 static void dnotify_signal_handler(int sig, siginfo_t *info, void *unused)
75 {
76         int saved_errno;
77
78         /*
79          * According to http://www.opengroup.org/onlinepubs/009695399/ write
80          * to a pipe either writes all or nothing, so we can safely write a
81          * full sizeof(int) and not risk the pipe to become out of sync with
82          * the receiving end.
83          *
84          * We don't care about the result of the write() call. If the pipe is
85          * full, then this signal is lost, we can't do anything about it.
86          */
87
88         saved_errno = errno;
89         write(dnotify_signal_pipe[1], (const void *)&info->si_fd, sizeof(int));
90         errno = saved_errno;
91
92         sys_select_signal(RT_SIGNAL_NOTIFY);
93 }
94
95 /****************************************************************************
96  The upper level handler informed when the pipe is ready for reading
97 *****************************************************************************/
98
99 static void dnotify_pipe_handler(struct event_context *event_ctx,
100                                  struct fd_event *event,
101                                  uint16 flags,
102                                  void *private_data)
103 {
104         int res, fd;
105         struct dnotify_ctx *ctx;
106
107         res = read(dnotify_signal_pipe[0], (void *)&fd, sizeof(int));
108
109         if (res == -1) {
110                 DEBUG(0, ("Read from the dnotify pipe failed: %s\n",
111                           strerror(errno)));
112                 TALLOC_FREE(event); /* Don't try again */
113                 return;
114         }
115
116         if (res != sizeof(int)) {
117                 smb_panic("read from dnotify pipe gave wrong number of "
118                           "bytes\n");
119         }
120
121         for (ctx = dnotify_list; ctx; ctx = ctx->next) {
122                 if (ctx->fd == fd) {
123                         notify_fsp(ctx->fsp, 0, NULL);
124                 }
125         }
126 }
127
128 /****************************************************************************
129  Register a change notify request.
130 *****************************************************************************/
131
132 static int kernel_register_notify(connection_struct *conn, char *path,
133                                   uint32 flags)
134 {
135         int fd;
136         unsigned long kernel_flags;
137         
138         fd = sys_open(path,O_RDONLY, 0);
139
140         if (fd == -1) {
141                 DEBUG(3,("Failed to open directory %s for change notify\n",
142                          path));
143                 return -1;
144         }
145
146         if (sys_fcntl_long(fd, F_SETSIG, RT_SIGNAL_NOTIFY) == -1) {
147                 DEBUG(3,("Failed to set signal handler for change notify\n"));
148                 close(fd);
149                 return -1;
150         }
151
152         kernel_flags = DN_CREATE|DN_DELETE|DN_RENAME; /* creation/deletion
153                                                        * changes
154                                                        * everything! */
155         if (flags & FILE_NOTIFY_CHANGE_FILE_NAME)   kernel_flags |= DN_MODIFY;
156         if (flags & FILE_NOTIFY_CHANGE_DIR_NAME)    kernel_flags
157                                                             |= DN_RENAME
158                                                             |DN_DELETE;
159         if (flags & FILE_NOTIFY_CHANGE_ATTRIBUTES)  kernel_flags |= DN_ATTRIB;
160         if (flags & FILE_NOTIFY_CHANGE_SIZE)        kernel_flags |= DN_MODIFY;
161         if (flags & FILE_NOTIFY_CHANGE_LAST_WRITE)  kernel_flags |= DN_MODIFY;
162         if (flags & FILE_NOTIFY_CHANGE_LAST_ACCESS) kernel_flags |= DN_ACCESS;
163         if (flags & FILE_NOTIFY_CHANGE_CREATION)    kernel_flags |= DN_CREATE;
164         if (flags & FILE_NOTIFY_CHANGE_SECURITY)    kernel_flags |= DN_ATTRIB;
165         if (flags & FILE_NOTIFY_CHANGE_EA)          kernel_flags |= DN_ATTRIB;
166         if (flags & FILE_NOTIFY_CHANGE_FILE_NAME)   kernel_flags
167                                                             |= DN_RENAME
168                                                             |DN_DELETE;
169
170         if (sys_fcntl_long(fd, F_NOTIFY, kernel_flags) == -1) {
171                 DEBUG(3,("Failed to set async flag for change notify\n"));
172                 close(fd);
173                 return -1;
174         }
175
176         DEBUG(3,("kernel change notify on %s (ntflags=0x%x flags=0x%x) "
177                  "fd=%d\n", path, (int)flags, (int)kernel_flags, fd));
178
179         return fd;
180 }
181
182 /****************************************************************************
183  See if the kernel supports change notify.
184 ****************************************************************************/
185
186 static BOOL kernel_notify_available(void) 
187 {
188         int fd, ret;
189         fd = open("/tmp", O_RDONLY);
190         if (fd == -1)
191                 return False; /* uggh! */
192         ret = sys_fcntl_long(fd, F_NOTIFY, 0);
193         close(fd);
194         return ret == 0;
195 }
196
197 static int dnotify_ctx_destructor(struct dnotify_ctx *ctx)
198 {
199         close(ctx->fd);
200         DLIST_REMOVE(dnotify_list, ctx);
201         return 0;
202 }
203
204 static void *kernel_notify_add(TALLOC_CTX *mem_ctx,
205                                struct event_context *event_ctx,
206                                files_struct *fsp,
207                                uint32 *filter)
208 {
209         struct dnotify_ctx *ctx;
210
211         if (!(ctx = TALLOC_P(mem_ctx, struct dnotify_ctx))) {
212                 DEBUG(0, ("talloc failed\n"));
213                 return NULL;
214         }
215
216         ctx->fsp = fsp;
217         ctx->fd = kernel_register_notify(fsp->conn, fsp->fsp_name, *filter);
218
219         if (ctx->fd == -1) {
220                 TALLOC_FREE(ctx);
221                 return NULL;
222         }
223
224         DLIST_ADD(dnotify_list, ctx);
225         talloc_set_destructor(ctx, dnotify_ctx_destructor);
226
227         return ctx;
228 }
229
230 /****************************************************************************
231  Setup kernel based change notify.
232 ****************************************************************************/
233
234 struct cnotify_fns *kernel_notify_init(struct event_context *event_ctx)
235 {
236         static struct cnotify_fns cnotify;
237         struct sigaction act;
238
239         if (pipe(dnotify_signal_pipe) == -1) {
240                 DEBUG(0, ("Failed to create signal pipe: %s\n",
241                           strerror(errno)));
242                 return NULL;
243         }
244
245         if ((set_blocking(dnotify_signal_pipe[0], False) == -1)
246             || (set_blocking(dnotify_signal_pipe[1], False) == -1)) {
247                 DEBUG(0, ("Failed to set signal pipe to non-blocking: %s\n",
248                           strerror(errno)));
249                 close(dnotify_signal_pipe[0]);
250                 close(dnotify_signal_pipe[1]);
251                 return NULL;
252         }
253
254         if (event_add_fd(event_ctx, NULL, dnotify_signal_pipe[0],
255                          EVENT_FD_READ, dnotify_pipe_handler, NULL) == NULL) {
256                 DEBUG(0, ("Failed to set signal event handler\n"));
257                 close(dnotify_signal_pipe[0]);
258                 close(dnotify_signal_pipe[1]);
259                 return NULL;
260         }
261
262         ZERO_STRUCT(act);
263
264         act.sa_sigaction = dnotify_signal_handler;
265         act.sa_flags = SA_SIGINFO;
266         sigemptyset( &act.sa_mask );
267         if (sigaction(RT_SIGNAL_NOTIFY, &act, NULL) != 0) {
268                 DEBUG(0,("Failed to setup RT_SIGNAL_NOTIFY handler\n"));
269                 return NULL;
270         }
271
272         if (!kernel_notify_available())
273                 return NULL;
274
275         cnotify.notify_add = kernel_notify_add;
276
277         /* the signal can start off blocked due to a bug in bash */
278         BlockSignals(False, RT_SIGNAL_NOTIFY);
279
280         return &cnotify;
281 }
282
283 #else
284  void notify_kernel_dummy(void);
285
286  void notify_kernel_dummy(void) {}
287 #endif /* HAVE_KERNEL_CHANGE_NOTIFY */