r20932: This is the basic infrastructure for inotify support. This is far from being
[tprouty/samba.git] / source / smbd / notify_inotify.c
1 /*
2  * inotify change notify support
3  *
4  * Copyright (c) Andrew Tridgell 2006
5  * Copyright (c) Volker Lendecke 2007
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "includes.h"
23
24 #include <linux/inotify.h>
25 #include <asm/unistd.h>
26
27 #ifdef HAVE_INOTIFY
28
29 #ifndef HAVE_INOTIFY_INIT
30 /*
31   glibc doesn't define these functions yet (as of March 2006)
32 */
33 static int inotify_init(void)
34 {
35         return syscall(__NR_inotify_init);
36 }
37
38 static int inotify_add_watch(int fd, const char *path, __u32 mask)
39 {
40         return syscall(__NR_inotify_add_watch, fd, path, mask);
41 }
42
43 static int inotify_rm_watch(int fd, int wd)
44 {
45         return syscall(__NR_inotify_rm_watch, fd, wd);
46 }
47 #endif
48
49 struct inotify_ctx {
50         struct inotify_ctx *prev, *next;
51         int wd;
52         files_struct *fsp;
53 };
54
55 static struct inotify_ctx *inotify_list;
56
57 static int inotify_watch_fd;
58
59 /*
60   map from a change notify mask to a inotify mask. Remove any bits
61   which we can handle
62 */
63 static const struct {
64         uint32_t notify_mask;
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}
77 };
78
79 static uint32_t inotify_map(uint32 *filter)
80 {
81         int i;
82         uint32_t out=0;
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;
87                 }
88         }
89         return out;
90 }
91
92 static int inotify_ctx_destructor(struct inotify_ctx *ctx)
93 {
94         if (inotify_rm_watch(inotify_watch_fd, ctx->wd) == -1) {
95                 DEBUG(0, ("inotify_rm_watch failed: %s\n", strerror(errno)));
96         }
97         DLIST_REMOVE(inotify_list, ctx);
98         return 0;
99 }
100
101 static void *inotify_add(TALLOC_CTX *mem_ctx, 
102                          struct event_context *event_ctx,
103                          files_struct *fsp, uint32 *pfilter)
104 {
105         struct inotify_ctx *ctx;
106         uint32_t inotify_mask;
107         pstring fullpath;
108         uint32 filter;
109         
110         filter = *pfilter;
111         if ((filter & (FILE_NOTIFY_CHANGE_FILE
112                        |FILE_NOTIFY_CHANGE_DIR_NAME)) == 0) {
113                 /*
114                  * This first implementation only looks at create/delete
115                  */
116                 return NULL;
117         }
118
119         inotify_mask = inotify_map(&filter);
120
121         if (inotify_mask == 0) {
122                 DEBUG(10, ("inotify_mask == 0, nothing to do\n"));
123                 return NULL;
124         }
125
126         pstrcpy(fullpath, fsp->fsp_name);
127         if (!canonicalize_path(fsp->conn, fullpath)) {
128                 DEBUG(0, ("failed to canonicalize path '%s'\n", fullpath));
129                 return NULL;
130         }
131
132         if (*fullpath != '/') {
133                 DEBUG(0, ("canonicalized path '%s' into `%s`\n", fsp->fsp_name,
134                           fullpath));
135                 DEBUGADD(0, ("but expected an absolute path\n"));
136                 return NULL;
137         }
138         
139         if (!(ctx = TALLOC_P(mem_ctx, struct inotify_ctx))) {
140                 DEBUG(0, ("talloc failed\n"));
141                 return NULL;
142         }
143
144         ctx->fsp = fsp;
145         ctx->wd = inotify_add_watch(inotify_watch_fd, fullpath, inotify_mask);
146
147         if (ctx->wd == -1) {
148                 DEBUG(5, ("inotify_add_watch failed: %s\n", strerror(errno)));
149                 TALLOC_FREE(ctx);
150                 return NULL;
151         }
152
153         DLIST_ADD(inotify_list, ctx);
154         talloc_set_destructor(ctx, inotify_ctx_destructor);
155
156         *pfilter = filter;
157         return ctx;
158 }
159
160 static void inotify_dispatch(struct inotify_event *e)
161 {
162         struct inotify_ctx *ctx;
163
164         for (ctx = inotify_list; ctx; ctx = ctx->next) {
165                 if (ctx->wd == e->wd) {
166                         break;
167                 }
168         }
169
170         if (ctx == NULL) {
171                 /* not found */
172                 return;
173         }
174
175         if (e->mask & IN_CREATE) {
176                 notify_fsp(ctx->fsp, NOTIFY_ACTION_ADDED, e->name);
177         }
178
179         if (e->mask & IN_DELETE) {
180                 notify_fsp(ctx->fsp, NOTIFY_ACTION_REMOVED, e->name);
181         }
182 }
183
184 static void inotify_callback(struct event_context *event_ctx,
185                              struct fd_event *event,
186                              uint16 flags,
187                              void *private_data)
188 {
189         char *buf, *p;
190         int bufsize;
191
192         /*
193           we must use FIONREAD as we cannot predict the length of the
194           filenames, and thus can't know how much to allocate
195           otherwise
196         */
197         if ((ioctl(inotify_watch_fd, FIONREAD, &bufsize) != 0)
198             || (bufsize == 0)) {
199                 DEBUG(0,("No data on inotify fd?!\n"));
200                 return;
201         }
202
203         if (!(buf = SMB_MALLOC_ARRAY(char, bufsize))) {
204                 DEBUG(0, ("malloc failed\n"));
205                 return;
206         }
207
208         if (read(inotify_watch_fd, buf, bufsize) != bufsize) {
209                 DEBUG(0,("Failed to read all inotify data\n"));
210                 SAFE_FREE(buf);
211                 return;
212         }
213
214         p = buf;
215
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;
219
220                 if ((len > bufsize)
221                     || ((iev->len != 0) && (iev->name[iev->len-1] != '\0'))) {
222                         smb_panic("invalid inotify reply\n");
223                 }
224
225                 inotify_dispatch(iev);
226
227                 p += len;
228                 bufsize -= len;
229         }
230
231         SAFE_FREE(buf);
232         return;
233 }
234
235 static struct cnotify_fns inotify_fns =
236 {
237     inotify_add,
238 };
239
240 struct cnotify_fns *inotify_notify_init(struct event_context *event_ctx)
241 {
242         inotify_watch_fd = inotify_init();
243
244         DEBUG(10, ("inotify_notify_init called\n"));
245
246         if (inotify_watch_fd == -1) {
247                 DEBUG(0, ("inotify_init failed: %s\n", strerror(errno)));
248                 return NULL;
249         }
250
251         if (event_add_fd(event_ctx, NULL, inotify_watch_fd,
252                          EVENT_FD_READ, inotify_callback,
253                          NULL) == NULL) {
254                 DEBUG(0, ("event_add_fd failed\n"));
255                 close(inotify_watch_fd);
256                 inotify_watch_fd = -1;
257                 return NULL;
258         }
259
260         return &inotify_fns;
261 }
262
263 #endif