2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2006
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 notify implementation using inotify
26 #include "system/filesys.h"
27 #include "ntvfs/sysdep/sys_notify.h"
28 #include "lib/events/events.h"
29 #include "lib/util/dlinklist.h"
30 #include "libcli/raw/smb.h"
32 #include <linux/inotify.h>
33 #include <asm/unistd.h>
35 #ifndef HAVE_INOTIFY_INIT
37 glibc doesn't define these functions yet (as of March 2006)
39 static int inotify_init(void)
41 return syscall(__NR_inotify_init
);
44 static int inotify_add_watch(int fd
, const char *path
, __u32 mask
)
46 return syscall(__NR_inotify_add_watch
, fd
, path
, mask
);
49 static int inotify_rm_watch(int fd
, int wd
)
51 return syscall(__NR_inotify_rm_watch
, fd
, wd
);
56 /* older glibc headers don't have these defines either */
58 #define IN_ONLYDIR 0x01000000
61 #define IN_MASK_ADD 0x20000000
64 struct inotify_private
{
65 struct sys_notify_context
*ctx
;
67 struct watch_context
*watches
;
70 struct watch_context
{
71 struct watch_context
*next
, *prev
;
72 struct inotify_private
*in
;
74 sys_notify_callback_t callback
;
76 uint32_t mask
; /* the inotify mask */
77 uint32_t filter
; /* the windows completion filter */
83 destroy the inotify private context
85 static int inotify_destructor(struct inotify_private
*in
)
93 see if a particular event from inotify really does match a requested
96 static BOOL
filter_match(struct watch_context
*w
, struct inotify_event
*e
)
98 if ((e
->mask
& w
->mask
) == 0) {
99 /* this happens because inotify_add_watch() coalesces watches on the same
100 path, oring their masks together */
104 /* SMB separates the filters for files and directories */
105 if (e
->mask
& IN_ISDIR
) {
106 if ((w
->filter
& FILE_NOTIFY_CHANGE_DIR_NAME
) == 0) {
110 if ((e
->mask
& IN_ATTRIB
) &&
111 (w
->filter
& (FILE_NOTIFY_CHANGE_ATTRIBUTES
|
112 FILE_NOTIFY_CHANGE_LAST_WRITE
|
113 FILE_NOTIFY_CHANGE_LAST_ACCESS
|
114 FILE_NOTIFY_CHANGE_EA
|
115 FILE_NOTIFY_CHANGE_SECURITY
))) {
118 if ((e
->mask
& IN_MODIFY
) &&
119 (w
->filter
& FILE_NOTIFY_CHANGE_ATTRIBUTES
)) {
122 if ((w
->filter
& FILE_NOTIFY_CHANGE_FILE_NAME
) == 0) {
133 dispatch one inotify event
135 the cookies are used to correctly handle renames
137 static void inotify_dispatch(struct inotify_private
*in
,
138 struct inotify_event
*e
,
139 uint32_t prev_cookie
,
140 struct inotify_event
*e2
)
142 struct watch_context
*w
, *next
;
143 struct notify_event ne
;
145 /* ignore extraneous events, such as unmount and IN_IGNORED events */
146 if ((e
->mask
& (IN_ATTRIB
|IN_MODIFY
|IN_CREATE
|IN_DELETE
|
147 IN_MOVED_FROM
|IN_MOVED_TO
)) == 0) {
151 /* map the inotify mask to a action. This gets complicated for
153 if (e
->mask
& IN_CREATE
) {
154 ne
.action
= NOTIFY_ACTION_ADDED
;
155 } else if (e
->mask
& IN_DELETE
) {
156 ne
.action
= NOTIFY_ACTION_REMOVED
;
157 } else if (e
->mask
& IN_MOVED_FROM
) {
158 if (e2
!= NULL
&& e2
->cookie
== e
->cookie
) {
159 ne
.action
= NOTIFY_ACTION_OLD_NAME
;
161 ne
.action
= NOTIFY_ACTION_REMOVED
;
163 } else if (e
->mask
& IN_MOVED_TO
) {
164 if (e
->cookie
== prev_cookie
) {
165 ne
.action
= NOTIFY_ACTION_NEW_NAME
;
167 ne
.action
= NOTIFY_ACTION_ADDED
;
170 ne
.action
= NOTIFY_ACTION_MODIFIED
;
174 /* find any watches that have this watch descriptor */
175 for (w
=in
->watches
;w
;w
=next
) {
177 if (w
->wd
== e
->wd
&& filter_match(w
, e
)) {
178 w
->callback(in
->ctx
, w
->private_data
, &ne
);
182 /* SMB expects a file rename to generate three events, two for
183 the rename and the other for a modify of the
184 destination. Strange! */
185 if (ne
.action
!= NOTIFY_ACTION_NEW_NAME
||
186 (e
->mask
& IN_ISDIR
) != 0) {
190 ne
.action
= NOTIFY_ACTION_MODIFIED
;
193 for (w
=in
->watches
;w
;w
=next
) {
195 if (w
->wd
== e
->wd
&& filter_match(w
, e
) &&
196 !(w
->filter
& FILE_NOTIFY_CHANGE_CREATION
)) {
197 w
->callback(in
->ctx
, w
->private_data
, &ne
);
203 called when the kernel has some events for us
205 static void inotify_handler(struct event_context
*ev
, struct fd_event
*fde
,
206 uint16_t flags
, void *private_data
)
208 struct inotify_private
*in
= talloc_get_type(private_data
,
209 struct inotify_private
);
211 struct inotify_event
*e0
, *e
;
212 uint32_t prev_cookie
=0;
215 we must use FIONREAD as we cannot predict the length of the
216 filenames, and thus can't know how much to allocate
219 if (ioctl(in
->fd
, FIONREAD
, &bufsize
) != 0 ||
221 DEBUG(0,("No data on inotify fd?!\n"));
225 e0
= e
= talloc_size(in
, bufsize
);
226 if (e
== NULL
) return;
228 if (read(in
->fd
, e0
, bufsize
) != bufsize
) {
229 DEBUG(0,("Failed to read all inotify data\n"));
234 /* we can get more than one event in the buffer */
235 while (bufsize
>= sizeof(*e
)) {
236 struct inotify_event
*e2
= NULL
;
237 bufsize
-= e
->len
+ sizeof(*e
);
238 if (bufsize
>= sizeof(*e
)) {
239 e2
= (struct inotify_event
*)(e
->len
+ sizeof(*e
) + (char *)e
);
241 inotify_dispatch(in
, e
, prev_cookie
, e2
);
242 prev_cookie
= e
->cookie
;
250 setup the inotify handle - called the first time a watch is added on
253 static NTSTATUS
inotify_setup(struct sys_notify_context
*ctx
)
255 struct inotify_private
*in
;
257 if (!lp_parm_bool(-1, "notify", "inotify", True
)) {
258 return NT_STATUS_INVALID_SYSTEM_SERVICE
;
261 in
= talloc(ctx
, struct inotify_private
);
262 NT_STATUS_HAVE_NO_MEMORY(in
);
263 in
->fd
= inotify_init();
265 DEBUG(0,("Failed to init inotify - %s\n", strerror(errno
)));
267 return map_nt_error_from_unix(errno
);
272 ctx
->private_data
= in
;
273 talloc_set_destructor(in
, inotify_destructor
);
275 /* add a event waiting for the inotify fd to be readable */
276 event_add_fd(ctx
->ev
, in
, in
->fd
, EVENT_FD_READ
, inotify_handler
, in
);
283 map from a change notify mask to a inotify mask. Remove any bits
286 static const struct {
287 uint32_t notify_mask
;
288 uint32_t inotify_mask
;
289 } inotify_mapping
[] = {
290 {FILE_NOTIFY_CHANGE_FILE_NAME
, IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
},
291 {FILE_NOTIFY_CHANGE_DIR_NAME
, IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
},
292 {FILE_NOTIFY_CHANGE_ATTRIBUTES
, IN_ATTRIB
|IN_MOVED_TO
|IN_MOVED_FROM
|IN_MODIFY
},
293 {FILE_NOTIFY_CHANGE_LAST_WRITE
, IN_ATTRIB
},
294 {FILE_NOTIFY_CHANGE_LAST_ACCESS
, IN_ATTRIB
},
295 {FILE_NOTIFY_CHANGE_EA
, IN_ATTRIB
},
296 {FILE_NOTIFY_CHANGE_SECURITY
, IN_ATTRIB
}
299 static uint32_t inotify_map(struct notify_entry
*e
)
303 for (i
=0;i
<ARRAY_SIZE(inotify_mapping
);i
++) {
304 if (inotify_mapping
[i
].notify_mask
& e
->filter
) {
305 out
|= inotify_mapping
[i
].inotify_mask
;
306 e
->filter
&= ~inotify_mapping
[i
].notify_mask
;
315 static int watch_destructor(struct watch_context
*w
)
317 struct inotify_private
*in
= w
->in
;
319 DLIST_REMOVE(w
->in
->watches
, w
);
321 /* only rm the watch if its the last one with this wd */
322 for (w
=in
->watches
;w
;w
=w
->next
) {
323 if (w
->wd
== wd
) break;
326 inotify_rm_watch(in
->fd
, wd
);
333 add a watch. The watch is removed when the caller calls
334 talloc_free() on *handle
336 static NTSTATUS
inotify_watch(struct sys_notify_context
*ctx
,
337 struct notify_entry
*e
,
338 sys_notify_callback_t callback
,
342 struct inotify_private
*in
;
345 struct watch_context
*w
;
346 uint32_t filter
= e
->filter
;
347 void **handle
= (void **)handle_p
;
349 /* maybe setup the inotify fd */
350 if (ctx
->private_data
== NULL
) {
352 status
= inotify_setup(ctx
);
353 NT_STATUS_NOT_OK_RETURN(status
);
356 in
= talloc_get_type(ctx
->private_data
, struct inotify_private
);
358 mask
= inotify_map(e
);
360 /* this filter can't be handled by inotify */
361 return NT_STATUS_INVALID_PARAMETER
;
364 /* using IN_MASK_ADD allows us to cope with inotify() returning the same
365 watch descriptor for muliple watches on the same path */
366 mask
|= (IN_MASK_ADD
| IN_ONLYDIR
);
368 /* get a new watch descriptor for this path */
369 wd
= inotify_add_watch(in
->fd
, e
->path
, mask
);
372 return map_nt_error_from_unix(errno
);
375 w
= talloc(in
, struct watch_context
);
377 inotify_rm_watch(in
->fd
, wd
);
379 return NT_STATUS_NO_MEMORY
;
384 w
->callback
= callback
;
385 w
->private_data
= private_data
;
388 w
->path
= talloc_strdup(w
, e
->path
);
389 if (w
->path
== NULL
) {
390 inotify_rm_watch(in
->fd
, wd
);
392 return NT_STATUS_NO_MEMORY
;
397 DLIST_ADD(in
->watches
, w
);
399 /* the caller frees the handle to stop watching */
400 talloc_set_destructor(w
, watch_destructor
);
406 static struct sys_notify_backend inotify
= {
408 .notify_watch
= inotify_watch
412 initialialise the inotify module
414 NTSTATUS
sys_notify_inotify_init(void)
416 /* register ourselves as a system inotify module */
417 return sys_notify_register(&inotify
);