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 3 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, see <http://www.gnu.org/licenses/>.
21 notify implementation using inotify
25 #include "system/filesys.h"
27 #include "ntvfs/sysdep/sys_notify.h"
28 #include "../lib/util/dlinklist.h"
29 #include "libcli/raw/smb.h"
30 #include "param/param.h"
32 #include <sys/inotify.h>
34 /* glibc < 2.5 headers don't have these defines */
36 #define IN_ONLYDIR 0x01000000
39 #define IN_MASK_ADD 0x20000000
42 struct inotify_private
{
43 struct sys_notify_context
*ctx
;
45 struct inotify_watch_context
*watches
;
48 struct inotify_watch_context
{
49 struct inotify_watch_context
*next
, *prev
;
50 struct inotify_private
*in
;
52 sys_notify_callback_t callback
;
54 uint32_t mask
; /* the inotify mask */
55 uint32_t filter
; /* the windows completion filter */
61 see if a particular event from inotify really does match a requested
64 static bool filter_match(struct inotify_watch_context
*w
,
65 struct inotify_event
*e
)
67 if ((e
->mask
& w
->mask
) == 0) {
68 /* this happens because inotify_add_watch() coalesces watches on the same
69 path, oring their masks together */
73 /* SMB separates the filters for files and directories */
74 if (e
->mask
& IN_ISDIR
) {
75 if ((w
->filter
& FILE_NOTIFY_CHANGE_DIR_NAME
) == 0) {
79 if ((e
->mask
& IN_ATTRIB
) &&
80 (w
->filter
& (FILE_NOTIFY_CHANGE_ATTRIBUTES
|
81 FILE_NOTIFY_CHANGE_LAST_WRITE
|
82 FILE_NOTIFY_CHANGE_LAST_ACCESS
|
83 FILE_NOTIFY_CHANGE_EA
|
84 FILE_NOTIFY_CHANGE_SECURITY
))) {
87 if ((e
->mask
& IN_MODIFY
) &&
88 (w
->filter
& FILE_NOTIFY_CHANGE_ATTRIBUTES
)) {
91 if ((w
->filter
& FILE_NOTIFY_CHANGE_FILE_NAME
) == 0) {
102 dispatch one inotify event
104 the cookies are used to correctly handle renames
106 static void inotify_dispatch(struct inotify_private
*in
,
107 struct inotify_event
*e
,
108 uint32_t prev_cookie
,
109 struct inotify_event
*e2
)
111 struct inotify_watch_context
*w
, *next
;
112 struct notify_event ne
;
114 /* ignore extraneous events, such as unmount and IN_IGNORED events */
115 if ((e
->mask
& (IN_ATTRIB
|IN_MODIFY
|IN_CREATE
|IN_DELETE
|
116 IN_MOVED_FROM
|IN_MOVED_TO
)) == 0) {
120 /* map the inotify mask to a action. This gets complicated for
122 if (e
->mask
& IN_CREATE
) {
123 ne
.action
= NOTIFY_ACTION_ADDED
;
124 } else if (e
->mask
& IN_DELETE
) {
125 ne
.action
= NOTIFY_ACTION_REMOVED
;
126 } else if (e
->mask
& IN_MOVED_FROM
) {
127 if (e2
!= NULL
&& e2
->cookie
== e
->cookie
) {
128 ne
.action
= NOTIFY_ACTION_OLD_NAME
;
130 ne
.action
= NOTIFY_ACTION_REMOVED
;
132 } else if (e
->mask
& IN_MOVED_TO
) {
133 if (e
->cookie
== prev_cookie
) {
134 ne
.action
= NOTIFY_ACTION_NEW_NAME
;
136 ne
.action
= NOTIFY_ACTION_ADDED
;
139 ne
.action
= NOTIFY_ACTION_MODIFIED
;
143 /* find any watches that have this watch descriptor */
144 for (w
=in
->watches
;w
;w
=next
) {
146 if (w
->wd
== e
->wd
&& filter_match(w
, e
)) {
147 w
->callback(in
->ctx
, w
->private_data
, &ne
);
151 /* SMB expects a file rename to generate three events, two for
152 the rename and the other for a modify of the
153 destination. Strange! */
154 if (ne
.action
!= NOTIFY_ACTION_NEW_NAME
||
155 (e
->mask
& IN_ISDIR
) != 0) {
159 ne
.action
= NOTIFY_ACTION_MODIFIED
;
162 for (w
=in
->watches
;w
;w
=next
) {
164 if (w
->wd
== e
->wd
&& filter_match(w
, e
) &&
165 !(w
->filter
& FILE_NOTIFY_CHANGE_CREATION
)) {
166 w
->callback(in
->ctx
, w
->private_data
, &ne
);
172 called when the kernel has some events for us
174 static void inotify_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
175 uint16_t flags
, void *private_data
)
177 struct inotify_private
*in
= talloc_get_type(private_data
,
178 struct inotify_private
);
180 struct inotify_event
*e0
, *e
;
181 uint32_t prev_cookie
=0;
184 we must use FIONREAD as we cannot predict the length of the
185 filenames, and thus can't know how much to allocate
188 if (ioctl(in
->fd
, FIONREAD
, &bufsize
) != 0 ||
190 DEBUG(0,("No data on inotify fd?!\n"));
194 e0
= e
= talloc_size(in
, bufsize
);
195 if (e
== NULL
) return;
197 if (read(in
->fd
, e0
, bufsize
) != bufsize
) {
198 DEBUG(0,("Failed to read all inotify data\n"));
203 /* we can get more than one event in the buffer */
204 while (bufsize
>= sizeof(*e
)) {
205 struct inotify_event
*e2
= NULL
;
206 bufsize
-= e
->len
+ sizeof(*e
);
207 if (bufsize
>= sizeof(*e
)) {
208 e2
= (struct inotify_event
*)(e
->len
+ sizeof(*e
) + (char *)e
);
210 inotify_dispatch(in
, e
, prev_cookie
, e2
);
211 prev_cookie
= e
->cookie
;
219 setup the inotify handle - called the first time a watch is added on
222 static NTSTATUS
inotify_setup(struct sys_notify_context
*ctx
)
224 struct inotify_private
*in
;
225 struct tevent_fd
*fde
;
227 in
= talloc(ctx
, struct inotify_private
);
228 NT_STATUS_HAVE_NO_MEMORY(in
);
230 in
->fd
= inotify_init();
232 DEBUG(0,("Failed to init inotify - %s\n", strerror(errno
)));
234 return map_nt_error_from_unix_common(errno
);
239 ctx
->private_data
= in
;
241 /* add a event waiting for the inotify fd to be readable */
242 fde
= tevent_add_fd(ctx
->ev
, in
, in
->fd
,
243 TEVENT_FD_READ
, inotify_handler
, in
);
248 DEBUG(0,("Failed to tevent_add_fd() - %s\n", strerror(errno
)));
250 return map_nt_error_from_unix_common(errno
);
253 tevent_fd_set_auto_close(fde
);
260 map from a change notify mask to a inotify mask. Remove any bits
263 static const struct {
264 uint32_t notify_mask
;
265 uint32_t inotify_mask
;
266 } inotify_mapping
[] = {
267 {FILE_NOTIFY_CHANGE_FILE_NAME
, IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
},
268 {FILE_NOTIFY_CHANGE_DIR_NAME
, IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
},
269 {FILE_NOTIFY_CHANGE_ATTRIBUTES
, IN_ATTRIB
|IN_MOVED_TO
|IN_MOVED_FROM
|IN_MODIFY
},
270 {FILE_NOTIFY_CHANGE_LAST_WRITE
, IN_ATTRIB
},
271 {FILE_NOTIFY_CHANGE_LAST_ACCESS
, IN_ATTRIB
},
272 {FILE_NOTIFY_CHANGE_EA
, IN_ATTRIB
},
273 {FILE_NOTIFY_CHANGE_SECURITY
, IN_ATTRIB
}
276 static uint32_t inotify_map(struct notify_entry
*e
)
280 for (i
=0;i
<ARRAY_SIZE(inotify_mapping
);i
++) {
281 if (inotify_mapping
[i
].notify_mask
& e
->filter
) {
282 out
|= inotify_mapping
[i
].inotify_mask
;
283 e
->filter
&= ~inotify_mapping
[i
].notify_mask
;
292 static int watch_destructor(struct inotify_watch_context
*w
)
294 struct inotify_private
*in
= w
->in
;
296 DLIST_REMOVE(w
->in
->watches
, w
);
298 /* only rm the watch if its the last one with this wd */
299 for (w
=in
->watches
;w
;w
=w
->next
) {
300 if (w
->wd
== wd
) break;
303 inotify_rm_watch(in
->fd
, wd
);
310 add a watch. The watch is removed when the caller calls
311 talloc_free() on *handle
313 static NTSTATUS
inotify_watch(struct sys_notify_context
*ctx
,
314 struct notify_entry
*e
,
315 sys_notify_callback_t callback
,
319 struct inotify_private
*in
;
322 struct inotify_watch_context
*w
;
323 uint32_t filter
= e
->filter
;
324 void **handle
= (void **)handle_p
;
326 /* maybe setup the inotify fd */
327 if (ctx
->private_data
== NULL
) {
329 status
= inotify_setup(ctx
);
330 NT_STATUS_NOT_OK_RETURN(status
);
333 in
= talloc_get_type(ctx
->private_data
, struct inotify_private
);
335 mask
= inotify_map(e
);
337 /* this filter can't be handled by inotify */
338 return NT_STATUS_INVALID_PARAMETER
;
341 /* using IN_MASK_ADD allows us to cope with inotify() returning the same
342 watch descriptor for muliple watches on the same path */
343 mask
|= (IN_MASK_ADD
| IN_ONLYDIR
);
345 /* get a new watch descriptor for this path */
346 wd
= inotify_add_watch(in
->fd
, e
->path
, mask
);
349 return map_nt_error_from_unix_common(errno
);
352 w
= talloc(in
, struct inotify_watch_context
);
354 inotify_rm_watch(in
->fd
, wd
);
356 return NT_STATUS_NO_MEMORY
;
361 w
->callback
= callback
;
362 w
->private_data
= private_data
;
365 w
->path
= talloc_strdup(w
, e
->path
);
366 if (w
->path
== NULL
) {
367 inotify_rm_watch(in
->fd
, wd
);
369 return NT_STATUS_NO_MEMORY
;
374 DLIST_ADD(in
->watches
, w
);
376 /* the caller frees the handle to stop watching */
377 talloc_set_destructor(w
, watch_destructor
);
383 static struct sys_notify_backend inotify
= {
385 .notify_watch
= inotify_watch
389 initialialise the inotify module
391 NTSTATUS
sys_notify_inotify_init(void)
393 /* register ourselves as a system inotify module */
394 return sys_notify_register(&inotify
);