2 * inotify change notify support
4 * Copyright (c) Andrew Tridgell 2006
5 * Copyright (c) Volker Lendecke 2007
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <linux/inotify.h>
25 #include <asm/unistd.h>
29 #ifndef HAVE_INOTIFY_INIT
31 glibc doesn't define these functions yet (as of March 2006)
33 static int inotify_init(void)
35 return syscall(__NR_inotify_init);
38 static int inotify_add_watch(int fd, const char *path, __u32 mask)
40 return syscall(__NR_inotify_add_watch, fd, path, mask);
43 static int inotify_rm_watch(int fd, int wd)
45 return syscall(__NR_inotify_rm_watch, fd, wd);
50 struct inotify_ctx *prev, *next;
55 static struct inotify_ctx *inotify_list;
57 static int inotify_watch_fd;
60 map from a change notify mask to a inotify mask. Remove any bits
65 uint32_t inotify_mask;
66 } inotify_mapping[] = {
67 {FILE_NOTIFY_CHANGE_FILE_NAME,
68 IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
69 {FILE_NOTIFY_CHANGE_DIR_NAME,
70 IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
71 {FILE_NOTIFY_CHANGE_ATTRIBUTES,
72 IN_ATTRIB|IN_MOVED_TO|IN_MOVED_FROM|IN_MODIFY},
73 {FILE_NOTIFY_CHANGE_LAST_WRITE, IN_ATTRIB},
74 {FILE_NOTIFY_CHANGE_LAST_ACCESS, IN_ATTRIB},
75 {FILE_NOTIFY_CHANGE_EA, IN_ATTRIB},
76 {FILE_NOTIFY_CHANGE_SECURITY, IN_ATTRIB}
79 static uint32_t inotify_map(uint32 *filter)
83 for (i=0;i<ARRAY_SIZE(inotify_mapping);i++) {
84 if (inotify_mapping[i].notify_mask & *filter) {
85 out |= inotify_mapping[i].inotify_mask;
86 *filter &= ~inotify_mapping[i].notify_mask;
92 static int inotify_ctx_destructor(struct inotify_ctx *ctx)
94 if (inotify_rm_watch(inotify_watch_fd, ctx->wd) == -1) {
95 DEBUG(0, ("inotify_rm_watch failed: %s\n", strerror(errno)));
97 DLIST_REMOVE(inotify_list, ctx);
101 static void *inotify_add(TALLOC_CTX *mem_ctx,
102 struct event_context *event_ctx,
103 files_struct *fsp, uint32 *pfilter)
105 struct inotify_ctx *ctx;
106 uint32_t inotify_mask;
111 if ((filter & (FILE_NOTIFY_CHANGE_FILE
112 |FILE_NOTIFY_CHANGE_DIR_NAME)) == 0) {
114 * This first implementation only looks at create/delete
119 inotify_mask = inotify_map(&filter);
121 if (inotify_mask == 0) {
122 DEBUG(10, ("inotify_mask == 0, nothing to do\n"));
126 pstrcpy(fullpath, fsp->fsp_name);
127 if (!canonicalize_path(fsp->conn, fullpath)) {
128 DEBUG(0, ("failed to canonicalize path '%s'\n", fullpath));
132 if (*fullpath != '/') {
133 DEBUG(0, ("canonicalized path '%s' into `%s`\n", fsp->fsp_name,
135 DEBUGADD(0, ("but expected an absolute path\n"));
139 if (!(ctx = TALLOC_P(mem_ctx, struct inotify_ctx))) {
140 DEBUG(0, ("talloc failed\n"));
145 ctx->wd = inotify_add_watch(inotify_watch_fd, fullpath, inotify_mask);
148 DEBUG(5, ("inotify_add_watch failed: %s\n", strerror(errno)));
153 DLIST_ADD(inotify_list, ctx);
154 talloc_set_destructor(ctx, inotify_ctx_destructor);
160 static void inotify_dispatch(struct inotify_event *e)
162 struct inotify_ctx *ctx;
164 for (ctx = inotify_list; ctx; ctx = ctx->next) {
165 if (ctx->wd == e->wd) {
175 if (e->mask & IN_CREATE) {
176 notify_fsp(ctx->fsp, NOTIFY_ACTION_ADDED, e->name);
179 if (e->mask & IN_DELETE) {
180 notify_fsp(ctx->fsp, NOTIFY_ACTION_REMOVED, e->name);
184 static void inotify_callback(struct event_context *event_ctx,
185 struct fd_event *event,
193 we must use FIONREAD as we cannot predict the length of the
194 filenames, and thus can't know how much to allocate
197 if ((ioctl(inotify_watch_fd, FIONREAD, &bufsize) != 0)
199 DEBUG(0,("No data on inotify fd?!\n"));
203 if (!(buf = SMB_MALLOC_ARRAY(char, bufsize))) {
204 DEBUG(0, ("malloc failed\n"));
208 if (read(inotify_watch_fd, buf, bufsize) != bufsize) {
209 DEBUG(0,("Failed to read all inotify data\n"));
216 while (bufsize > sizeof(struct inotify_event)) {
217 struct inotify_event *iev = (struct inotify_event *)p;
218 size_t len = sizeof(struct inotify_event) + iev->len;
221 || ((iev->len != 0) && (iev->name[iev->len-1] != '\0'))) {
222 smb_panic("invalid inotify reply\n");
225 inotify_dispatch(iev);
235 static struct cnotify_fns inotify_fns =
240 struct cnotify_fns *inotify_notify_init(struct event_context *event_ctx)
242 inotify_watch_fd = inotify_init();
244 DEBUG(10, ("inotify_notify_init called\n"));
246 if (inotify_watch_fd == -1) {
247 DEBUG(0, ("inotify_init failed: %s\n", strerror(errno)));
251 if (event_add_fd(event_ctx, NULL, inotify_watch_fd,
252 EVENT_FD_READ, inotify_callback,
254 DEBUG(0, ("event_add_fd failed\n"));
255 close(inotify_watch_fd);
256 inotify_watch_fd = -1;