Tizen 2.1 base
[platform/upstream/gcd.git] / kqueue-1.0.4 / src / linux / vnode.c
1 /*
2  * Copyright (c) 2009 Mark Heily <mark@heily.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <errno.h>
18 #include <err.h>
19 #include <fcntl.h>
20 #include <pthread.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <sys/queue.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <limits.h>
32 #include <sys/inotify.h>
33 #include <sys/epoll.h>
34
35 #include "sys/event.h"
36 #include "private.h"
37
38 static char *
39 inotify_mask_dump(uint32_t mask)
40 {
41     static char __thread buf[1024];
42
43 #define INEVT_MASK_DUMP(attrib) \
44     if (mask & attrib) \
45        strcat(buf, #attrib" ");
46
47     snprintf(buf, sizeof(buf), "mask = %d (", mask);
48     INEVT_MASK_DUMP(IN_ACCESS);
49     INEVT_MASK_DUMP(IN_MODIFY);
50     INEVT_MASK_DUMP(IN_ATTRIB);
51     INEVT_MASK_DUMP(IN_CLOSE_WRITE);
52     INEVT_MASK_DUMP(IN_CLOSE_NOWRITE);
53     INEVT_MASK_DUMP(IN_OPEN);
54     INEVT_MASK_DUMP(IN_MOVED_FROM);
55     INEVT_MASK_DUMP(IN_MOVED_TO);
56     INEVT_MASK_DUMP(IN_CREATE);
57     INEVT_MASK_DUMP(IN_DELETE);
58     INEVT_MASK_DUMP(IN_DELETE_SELF);
59     INEVT_MASK_DUMP(IN_MOVE_SELF);
60     buf[strlen(buf) - 1] = ')';
61
62     return (buf);
63 }
64
65 static char *
66 inotify_event_dump(struct inotify_event *evt)
67 {
68     static char __thread buf[1024];
69
70     snprintf(buf, sizeof(buf), "wd=%d mask=%s", 
71             evt->wd,
72             inotify_mask_dump(evt->mask));
73
74     return (buf);
75 }
76
77 static int
78 fd_to_path(char *buf, size_t bufsz, int fd)
79 {
80     char path[1024];    //TODO: Maxpathlen, etc.
81
82     if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0)
83         return (-1);
84
85     memset(buf, 0, bufsz);
86     return (readlink(path, buf, bufsz));
87 }
88
89
90 /* TODO: USE this to get events with name field */
91 int
92 get_one_event(struct inotify_event *dst, int pfd)
93 {
94     ssize_t n;
95
96     dbg_puts("reading one inotify event");
97     for (;;) {
98         n = read(pfd, dst, sizeof(*dst));
99         if (n < 0) {
100             if (errno == EINTR)
101                 continue;
102             dbg_perror("read");
103             return (-1);
104         } else {
105             break;
106         }
107     }
108     dbg_printf("read(2) from inotify wd: %zu bytes", n);
109
110     /* FIXME-TODO: if len > 0, read(len) */
111     if (dst->len != 0) 
112         abort();
113
114
115     return (0);
116 }
117
118 static int
119 add_watch(struct filter *filt, struct knote *kn)
120 {
121     char path[PATH_MAX];
122     uint32_t mask;
123
124     /* Convert the fd to a pathname */
125     if (fd_to_path(&path[0], sizeof(path), kn->kev.ident) < 0)
126         return (-1);
127
128     /* Convert the fflags to the inotify mask */
129     mask = 0;
130     if (kn->kev.fflags & NOTE_DELETE)
131         mask |= IN_ATTRIB | IN_DELETE_SELF;
132     if (kn->kev.fflags & NOTE_WRITE)      
133         mask |= IN_MODIFY | IN_ATTRIB;
134     if (kn->kev.fflags & NOTE_EXTEND)
135         mask |= IN_MODIFY | IN_ATTRIB;
136     if ((kn->kev.fflags & NOTE_ATTRIB) || 
137             (kn->kev.fflags & NOTE_LINK))
138         mask |= IN_ATTRIB;
139     if (kn->kev.fflags & NOTE_RENAME)
140         mask |= IN_MOVE_SELF;
141     if (kn->kev.flags & EV_ONESHOT)
142         mask |= IN_ONESHOT;
143
144     dbg_printf("inotify_add_watch(2); inofd=%d, %s, path=%s", 
145             filt->kf_pfd, inotify_mask_dump(mask), path);
146     kn->kev.data = inotify_add_watch(filt->kf_pfd, path, mask);
147     if (kn->kev.data < 0) {
148         dbg_printf("inotify_add_watch(2): %s", strerror(errno));
149         return (-1);
150     }
151     return (0);
152 }
153
154 static int
155 delete_watch(struct filter *filt, struct knote *kn)
156 {
157     if (kn->kev.data < 0)
158         return (0);
159     if (inotify_rm_watch(filt->kf_pfd, kn->kev.data) < 0) {
160         dbg_printf("inotify_rm_watch(2): %s", strerror(errno));
161         return (-1);
162     }
163     dbg_printf("wd %d removed", (int) kn->kev.data);
164     kn->kev.data = -1;
165
166     return (0);
167 }
168
169 int
170 evfilt_vnode_init(struct filter *filt)
171 {
172     filt->kf_pfd = inotify_init();
173     dbg_printf("inotify fd = %d", filt->kf_pfd);
174     if (filt->kf_pfd < 0)
175         return (-1);
176
177     return (0);
178 }
179
180 void
181 evfilt_vnode_destroy(struct filter *filt)
182 {
183     close(filt->kf_pfd);
184 }
185
186 int
187 evfilt_vnode_copyout(struct filter *filt, 
188             struct kevent *dst, 
189             int nevents)
190 {
191     struct inotify_event evt;
192     struct stat sb;
193     struct knote *kn;
194
195     if (get_one_event(&evt, filt->kf_pfd) < 0)
196         return (-1);
197
198     dbg_printf("inotify event: %s", inotify_event_dump(&evt));
199     if (evt.mask & IN_IGNORED) {
200         /* TODO: possibly return error when fs is unmounted */
201         return (0);
202     }
203
204     kn = knote_lookup_data(filt, evt.wd);
205     if (kn == NULL) {
206         dbg_printf("no match for wd # %d", evt.wd);
207         return (-1);
208     }
209
210     memcpy(dst, &kn->kev, sizeof(*dst));
211     dst->data = 0;
212
213     /* No error checking because fstat(2) should rarely fail */
214     //FIXME: EINTR
215     if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY) 
216         && fstat(kn->kev.ident, &sb) == 0) {
217         if (sb.st_nlink == 0 && kn->kev.fflags & NOTE_DELETE) 
218             dst->fflags |= NOTE_DELETE;
219         if (sb.st_nlink != kn->data.vnode.nlink && kn->kev.fflags & NOTE_LINK) 
220             dst->fflags |= NOTE_LINK;
221 #if HAVE_NOTE_TRUNCATE
222         if (sb.st_nsize == 0 && kn->kev.fflags & NOTE_TRUNCATE) 
223             dst->fflags |= NOTE_TRUNCATE;
224 #endif
225         if (sb.st_size > kn->data.vnode.size && kn->kev.fflags & NOTE_WRITE) 
226             dst->fflags |= NOTE_EXTEND;
227        kn->data.vnode.nlink = sb.st_nlink;
228        kn->data.vnode.size = sb.st_size;
229     }
230
231     if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE) 
232         dst->fflags |= NOTE_WRITE;
233     if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB) 
234         dst->fflags |= NOTE_ATTRIB;
235     if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME) 
236         dst->fflags |= NOTE_RENAME;
237     if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE) 
238         dst->fflags |= NOTE_DELETE;
239
240     if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE) 
241         dst->fflags |= NOTE_WRITE;
242     if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB) 
243         dst->fflags |= NOTE_ATTRIB;
244     if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME) 
245         dst->fflags |= NOTE_RENAME;
246     if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE) 
247         dst->fflags |= NOTE_DELETE;
248
249     if (kn->kev.flags & EV_DISPATCH) {
250         delete_watch(filt, kn); /* TODO: error checking */
251         KNOTE_DISABLE(kn);
252     } else if (kn->kev.flags & EV_ONESHOT) {
253         delete_watch(filt, kn); /* TODO: error checking */
254         knote_free(filt, kn);
255     }
256             
257     return (1);
258 }
259
260 int
261 evfilt_vnode_knote_create(struct filter *filt, struct knote *kn)
262 {
263     struct stat sb;
264
265     if (fstat(kn->kev.ident, &sb) < 0) {
266         dbg_puts("fstat failed");
267         return (-1);
268     }
269     kn->data.vnode.nlink = sb.st_nlink;
270     kn->data.vnode.size = sb.st_size;
271     kn->kev.data = -1;
272
273     return (add_watch(filt, kn));
274 }
275
276 int
277 evfilt_vnode_knote_modify(struct filter *filt, struct knote *kn, 
278         const struct kevent *kev)
279 {
280     return (-1); /* FIXME - STUB */
281 }
282
283 int
284 evfilt_vnode_knote_delete(struct filter *filt, struct knote *kn)
285 {   
286     return delete_watch(filt, kn);
287 }
288
289 int
290 evfilt_vnode_knote_enable(struct filter *filt, struct knote *kn)
291 {
292     return add_watch(filt, kn);
293 }
294
295 int
296 evfilt_vnode_knote_disable(struct filter *filt, struct knote *kn)
297 {
298     return delete_watch(filt, kn);
299 }
300
301 const struct filter evfilt_vnode = {
302     EVFILT_VNODE,
303     evfilt_vnode_init,
304     evfilt_vnode_destroy,
305     evfilt_vnode_copyout,
306     evfilt_vnode_knote_create,
307     evfilt_vnode_knote_modify,
308     evfilt_vnode_knote_delete,
309     evfilt_vnode_knote_enable,
310     evfilt_vnode_knote_disable,        
311 };