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
)) {
148 w
->callback(in
->ctx
, w
->private_data
, &ne
);
152 /* SMB expects a file rename to generate three events, two for
153 the rename and the other for a modify of the
154 destination. Strange! */
155 if (ne
.action
!= NOTIFY_ACTION_NEW_NAME
||
156 (e
->mask
& IN_ISDIR
) != 0) {
160 ne
.action
= NOTIFY_ACTION_MODIFIED
;
163 for (w
=in
->watches
;w
;w
=next
) {
165 if (w
->wd
== e
->wd
&& filter_match(w
, e
) &&
166 !(w
->filter
& FILE_NOTIFY_CHANGE_CREATION
)) {
168 w
->callback(in
->ctx
, w
->private_data
, &ne
);
174 called when the kernel has some events for us
176 static void inotify_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
177 uint16_t flags
, void *private_data
)
179 struct inotify_private
*in
= talloc_get_type(private_data
,
180 struct inotify_private
);
182 struct inotify_event
*e0
, *e
;
183 uint32_t prev_cookie
=0;
186 we must use FIONREAD as we cannot predict the length of the
187 filenames, and thus can't know how much to allocate
190 if (ioctl(in
->fd
, FIONREAD
, &bufsize
) != 0 ||
192 DEBUG(0,("No data on inotify fd?!\n"));
196 e0
= e
= talloc_size(in
, bufsize
);
197 if (e
== NULL
) return;
199 if (read(in
->fd
, e0
, bufsize
) != bufsize
) {
200 DEBUG(0,("Failed to read all inotify data\n"));
205 /* we can get more than one event in the buffer */
206 while (bufsize
>= sizeof(*e
)) {
207 struct inotify_event
*e2
= NULL
;
208 bufsize
-= e
->len
+ sizeof(*e
);
209 if (bufsize
>= sizeof(*e
)) {
210 e2
= (struct inotify_event
*)(e
->len
+ sizeof(*e
) + (char *)e
);
212 inotify_dispatch(in
, e
, prev_cookie
, e2
);
213 prev_cookie
= e
->cookie
;
221 setup the inotify handle - called the first time a watch is added on
224 static NTSTATUS
inotify_setup(struct sys_notify_context
*ctx
)
226 struct inotify_private
*in
;
227 struct tevent_fd
*fde
;
229 in
= talloc(ctx
, struct inotify_private
);
230 NT_STATUS_HAVE_NO_MEMORY(in
);
232 in
->fd
= inotify_init();
234 DEBUG(0,("Failed to init inotify - %s\n", strerror(errno
)));
236 return map_nt_error_from_unix_common(errno
);
241 ctx
->private_data
= in
;
243 /* add a event waiting for the inotify fd to be readable */
244 fde
= tevent_add_fd(ctx
->ev
, in
, in
->fd
,
245 TEVENT_FD_READ
, inotify_handler
, in
);
250 DEBUG(0,("Failed to tevent_add_fd() - %s\n", strerror(errno
)));
252 return map_nt_error_from_unix_common(errno
);
255 tevent_fd_set_auto_close(fde
);
262 map from a change notify mask to a inotify mask. Remove any bits
265 static const struct {
266 uint32_t notify_mask
;
267 uint32_t inotify_mask
;
268 } inotify_mapping
[] = {
269 {FILE_NOTIFY_CHANGE_FILE_NAME
, IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
},
270 {FILE_NOTIFY_CHANGE_DIR_NAME
, IN_CREATE
|IN_DELETE
|IN_MOVED_FROM
|IN_MOVED_TO
},
271 {FILE_NOTIFY_CHANGE_ATTRIBUTES
, IN_ATTRIB
|IN_MOVED_TO
|IN_MOVED_FROM
|IN_MODIFY
},
272 {FILE_NOTIFY_CHANGE_LAST_WRITE
, IN_ATTRIB
},
273 {FILE_NOTIFY_CHANGE_LAST_ACCESS
, IN_ATTRIB
},
274 {FILE_NOTIFY_CHANGE_EA
, IN_ATTRIB
},
275 {FILE_NOTIFY_CHANGE_SECURITY
, IN_ATTRIB
}
278 static uint32_t inotify_map(struct notify_entry
*e
)
282 for (i
=0;i
<ARRAY_SIZE(inotify_mapping
);i
++) {
283 if (inotify_mapping
[i
].notify_mask
& e
->filter
) {
284 out
|= inotify_mapping
[i
].inotify_mask
;
285 e
->filter
&= ~inotify_mapping
[i
].notify_mask
;
294 static int watch_destructor(struct inotify_watch_context
*w
)
296 struct inotify_private
*in
= w
->in
;
298 DLIST_REMOVE(w
->in
->watches
, w
);
300 /* only rm the watch if its the last one with this wd */
301 for (w
=in
->watches
;w
;w
=w
->next
) {
302 if (w
->wd
== wd
) break;
305 inotify_rm_watch(in
->fd
, wd
);
312 add a watch. The watch is removed when the caller calls
313 talloc_free() on *handle
315 static NTSTATUS
inotify_watch(struct sys_notify_context
*ctx
,
316 struct notify_entry
*e
,
317 sys_notify_callback_t callback
,
321 struct inotify_private
*in
;
324 struct inotify_watch_context
*w
;
325 uint32_t filter
= e
->filter
;
326 void **handle
= (void **)handle_p
;
328 /* maybe setup the inotify fd */
329 if (ctx
->private_data
== NULL
) {
331 status
= inotify_setup(ctx
);
332 NT_STATUS_NOT_OK_RETURN(status
);
335 in
= talloc_get_type(ctx
->private_data
, struct inotify_private
);
337 mask
= inotify_map(e
);
339 /* this filter can't be handled by inotify */
340 return NT_STATUS_INVALID_PARAMETER
;
343 /* using IN_MASK_ADD allows us to cope with inotify() returning the same
344 watch descriptor for muliple watches on the same path */
345 mask
|= (IN_MASK_ADD
| IN_ONLYDIR
);
347 /* get a new watch descriptor for this path */
348 wd
= inotify_add_watch(in
->fd
, e
->path
, mask
);
351 return map_nt_error_from_unix_common(errno
);
354 w
= talloc(in
, struct inotify_watch_context
);
356 inotify_rm_watch(in
->fd
, wd
);
358 return NT_STATUS_NO_MEMORY
;
363 w
->callback
= callback
;
364 w
->private_data
= private_data
;
367 w
->path
= talloc_strdup(w
, e
->path
);
368 if (w
->path
== NULL
) {
369 inotify_rm_watch(in
->fd
, wd
);
371 return NT_STATUS_NO_MEMORY
;
376 DLIST_ADD(in
->watches
, w
);
378 /* the caller frees the handle to stop watching */
379 talloc_set_destructor(w
, watch_destructor
);
385 static struct sys_notify_backend inotify
= {
387 .notify_watch
= inotify_watch
391 initialialise the inotify module
393 NTSTATUS
sys_notify_inotify_init(void);
394 NTSTATUS
sys_notify_inotify_init(void)
396 /* register ourselves as a system inotify module */
397 return sys_notify_register(&inotify
);