Line 0
Link Here
|
|
|
1 |
# gio: Always purge kqueue subs from missing list |
2 |
# https://github.com/GNOME/glib/commit/e305fe971e4647d971428a772b7290b9c308a96f |
3 |
# kqueue: Fix invalid emission of G_FILE_MONITOR_EVENT_MOVED event |
4 |
# https://github.com/GNOME/glib/commit/76072a2dde4a4acc8be8d3c47efbc6811ebe0c1e |
5 |
# kqueue: Multiple fixes and simplifications |
6 |
# https://github.com/GNOME/glib/commit/aa39a0557c679fc345b0ba72a87c33152eb8ebcd |
7 |
# gpollfilemonitor: Fix use-after-free caused by leaking GSource |
8 |
# https://github.com/GNOME/glib/commit/ba4a9538e14e8ba0ea037cab5f4b23aa47272a4c |
9 |
# Convert gio/kqueue/Makefile.am to gio/kqueue/Makefile.in patch |
10 |
|
11 |
--- gio/gpollfilemonitor.c.orig 2016-10-22 05:17:34 UTC |
12 |
+++ gio/gpollfilemonitor.c |
13 |
@@ -50,7 +50,9 @@ g_poll_file_monitor_finalize (GObject* o |
14 |
|
15 |
poll_monitor = G_POLL_FILE_MONITOR (object); |
16 |
|
17 |
+ g_poll_file_monitor_cancel (G_FILE_MONITOR (poll_monitor)); |
18 |
g_object_unref (poll_monitor->file); |
19 |
+ g_clear_object (&poll_monitor->last_info); |
20 |
|
21 |
G_OBJECT_CLASS (g_poll_file_monitor_parent_class)->finalize (object); |
22 |
} |
23 |
--- gio/kqueue/Makefile.in.orig 2017-02-13 15:22:04 UTC |
24 |
+++ gio/kqueue/Makefile.in |
25 |
@@ -183,9 +183,8 @@ LTLIBRARIES = $(installed_test_LTLIBRARI |
26 |
libkqueue_la_LIBADD = |
27 |
am__objects_1 = |
28 |
am_libkqueue_la_OBJECTS = libkqueue_la-gkqueuefilemonitor.lo \ |
29 |
- libkqueue_la-kqueue-helper.lo libkqueue_la-kqueue-thread.lo \ |
30 |
- libkqueue_la-kqueue-sub.lo libkqueue_la-kqueue-missing.lo \ |
31 |
- libkqueue_la-kqueue-utils.lo libkqueue_la-kqueue-exclusions.lo \ |
32 |
+ libkqueue_la-kqueue-helper.lo \ |
33 |
+ libkqueue_la-kqueue-missing.lo \ |
34 |
libkqueue_la-dep-list.lo $(am__objects_1) |
35 |
libkqueue_la_OBJECTS = $(am_libkqueue_la_OBJECTS) |
36 |
AM_V_lt = $(am__v_lt_@AM_V@) |
37 |
@@ -778,19 +777,9 @@ all_test_ltlibs = $(test_ltlibraries) $( |
38 |
@ENABLE_INSTALLED_TESTS_TRUE@installed_test_meta_DATA = $(installed_testcases:=.test) |
39 |
libkqueue_la_SOURCES = \ |
40 |
gkqueuefilemonitor.c \ |
41 |
- gkqueuefilemonitor.h \ |
42 |
kqueue-helper.c \ |
43 |
kqueue-helper.h \ |
44 |
- kqueue-thread.c \ |
45 |
- kqueue-thread.h \ |
46 |
- kqueue-sub.c \ |
47 |
- kqueue-sub.h \ |
48 |
kqueue-missing.c \ |
49 |
- kqueue-missing.h \ |
50 |
- kqueue-utils.c \ |
51 |
- kqueue-utils.h \ |
52 |
- kqueue-exclusions.c \ |
53 |
- kqueue-exclusions.h \ |
54 |
dep-list.c \ |
55 |
dep-list.h \ |
56 |
$(NULL) |
57 |
--- gio/kqueue/gkqueuefilemonitor.c.orig 2016-10-22 05:18:22 UTC |
58 |
+++ gio/kqueue/gkqueuefilemonitor.c |
59 |
@@ -22,33 +22,73 @@ |
60 |
|
61 |
#include "config.h" |
62 |
|
63 |
-#include "gkqueuefilemonitor.h" |
64 |
-#include "kqueue-helper.h" |
65 |
-#include "kqueue-exclusions.h" |
66 |
+#include <sys/types.h> |
67 |
+#include <sys/event.h> |
68 |
+#include <sys/time.h> |
69 |
+#include <sys/socket.h> |
70 |
+#include <sys/stat.h> |
71 |
+ |
72 |
+#include <errno.h> |
73 |
+#include <fcntl.h> |
74 |
+#include <string.h> |
75 |
+ |
76 |
+#include <glib-object.h> |
77 |
+#include <gio/gfilemonitor.h> |
78 |
+#include <gio/glocalfilemonitor.h> |
79 |
+#include <gio/giomodule.h> |
80 |
#include <gio/gpollfilemonitor.h> |
81 |
#include <gio/gfile.h> |
82 |
-#include <gio/giomodule.h> |
83 |
+#include <glib-unix.h> |
84 |
+#include "glib-private.h" |
85 |
|
86 |
+#include "kqueue-helper.h" |
87 |
+#include "dep-list.h" |
88 |
|
89 |
-struct _GKqueueFileMonitor |
90 |
+G_LOCK_DEFINE_STATIC (kq_lock); |
91 |
+static GSource *kq_source; |
92 |
+static int kq_queue = -1; |
93 |
+ |
94 |
+#define G_TYPE_KQUEUE_FILE_MONITOR (g_kqueue_file_monitor_get_type ()) |
95 |
+#define G_KQUEUE_FILE_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ |
96 |
+ G_TYPE_KQUEUE_FILE_MONITOR, GKqueueFileMonitor)) |
97 |
+ |
98 |
+typedef GLocalFileMonitorClass GKqueueFileMonitorClass; |
99 |
+ |
100 |
+typedef struct |
101 |
{ |
102 |
GLocalFileMonitor parent_instance; |
103 |
|
104 |
kqueue_sub *sub; |
105 |
- |
106 |
+#ifndef O_EVTONLY |
107 |
GFileMonitor *fallback; |
108 |
GFile *fbfile; |
109 |
-}; |
110 |
+#endif |
111 |
+} GKqueueFileMonitor; |
112 |
+ |
113 |
+GType g_kqueue_file_monitor_get_type (void); |
114 |
+G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR, |
115 |
+ g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, |
116 |
+ g_define_type_id, |
117 |
+ "kqueue", |
118 |
+ 20)) |
119 |
+ |
120 |
+#ifndef O_EVTONLY |
121 |
+#define O_KQFLAG O_RDONLY |
122 |
+#else |
123 |
+#define O_KQFLAG O_EVTONLY |
124 |
+#endif |
125 |
+ |
126 |
+#define NOTE_ALL (NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_RENAME) |
127 |
|
128 |
static gboolean g_kqueue_file_monitor_cancel (GFileMonitor* monitor); |
129 |
+static gboolean g_kqueue_file_monitor_is_supported (void); |
130 |
|
131 |
-G_DEFINE_TYPE_WITH_CODE (GKqueueFileMonitor, g_kqueue_file_monitor, G_TYPE_LOCAL_FILE_MONITOR, |
132 |
- g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, |
133 |
- g_define_type_id, |
134 |
- "kqueue", |
135 |
- 20)) |
136 |
+static kqueue_sub *_kqsub_new (const gchar *, GLocalFileMonitor *, GFileMonitorSource *); |
137 |
+static void _kqsub_free (kqueue_sub *); |
138 |
+static gboolean _kqsub_cancel (kqueue_sub *); |
139 |
|
140 |
|
141 |
+#ifndef O_EVTONLY |
142 |
static void |
143 |
_fallback_callback (GFileMonitor *unused, |
144 |
GFile *first, |
145 |
@@ -57,21 +97,41 @@ _fallback_callback (GFileMonitor *u |
146 |
gpointer udata) |
147 |
{ |
148 |
GKqueueFileMonitor *kq_mon = G_KQUEUE_FILE_MONITOR (udata); |
149 |
- GFileMonitor *mon = G_FILE_MONITOR (kq_mon); |
150 |
- g_assert (kq_mon != NULL); |
151 |
- g_assert (mon != NULL); |
152 |
- (void) unused; |
153 |
- |
154 |
- if (event == G_FILE_MONITOR_EVENT_CHANGED) |
155 |
- { |
156 |
- GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (kq_mon); |
157 |
|
158 |
- _kh_dir_diff (kq_mon->sub, local_monitor->source); |
159 |
- } |
160 |
- else |
161 |
- g_file_monitor_emit_event (mon, first, second, event); |
162 |
+ g_file_monitor_emit_event (G_FILE_MONITOR (kq_mon), first, second, event); |
163 |
} |
164 |
|
165 |
+/* |
166 |
+ * _ke_is_excluded: |
167 |
+ * @full_path - a path to file to check. |
168 |
+ * |
169 |
+ * Returns: TRUE if the file should be excluded from the kqueue-powered |
170 |
+ * monitoring, FALSE otherwise. |
171 |
+ **/ |
172 |
+gboolean |
173 |
+_ke_is_excluded (const char *full_path) |
174 |
+{ |
175 |
+ GFile *f = NULL; |
176 |
+ GMount *mount = NULL; |
177 |
+ |
178 |
+ f = g_file_new_for_path (full_path); |
179 |
+ |
180 |
+ if (f != NULL) { |
181 |
+ mount = g_file_find_enclosing_mount (f, NULL, NULL); |
182 |
+ g_object_unref (f); |
183 |
+ } |
184 |
+ |
185 |
+ if ((mount != NULL && (g_mount_can_unmount (mount))) || g_str_has_prefix (full_path, "/mnt/")) |
186 |
+ { |
187 |
+ g_warning ("Excluding %s from kernel notification, falling back to poll", full_path); |
188 |
+ if (mount) |
189 |
+ g_object_unref (mount); |
190 |
+ return TRUE; |
191 |
+ } |
192 |
+ |
193 |
+ return FALSE; |
194 |
+} |
195 |
+#endif /* !O_EVTONLY */ |
196 |
|
197 |
static void |
198 |
g_kqueue_file_monitor_finalize (GObject *object) |
199 |
@@ -80,16 +140,18 @@ g_kqueue_file_monitor_finalize (GObject |
200 |
|
201 |
if (kqueue_monitor->sub) |
202 |
{ |
203 |
- _kh_cancel_sub (kqueue_monitor->sub); |
204 |
- _kh_sub_free (kqueue_monitor->sub); |
205 |
+ _kqsub_cancel (kqueue_monitor->sub); |
206 |
+ _kqsub_free (kqueue_monitor->sub); |
207 |
kqueue_monitor->sub = NULL; |
208 |
} |
209 |
|
210 |
+#ifndef O_EVTONLY |
211 |
if (kqueue_monitor->fallback) |
212 |
g_object_unref (kqueue_monitor->fallback); |
213 |
|
214 |
if (kqueue_monitor->fbfile) |
215 |
g_object_unref (kqueue_monitor->fbfile); |
216 |
+#endif |
217 |
|
218 |
if (G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) |
219 |
(*G_OBJECT_CLASS (g_kqueue_file_monitor_parent_class)->finalize) (object); |
220 |
@@ -103,21 +165,25 @@ g_kqueue_file_monitor_start (GLocalFileM |
221 |
GFileMonitorSource *source) |
222 |
{ |
223 |
GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (local_monitor); |
224 |
- GObject *obj; |
225 |
- GKqueueFileMonitorClass *klass; |
226 |
- GObjectClass *parent_class; |
227 |
- kqueue_sub *sub = NULL; |
228 |
- gboolean ret_kh_startup = FALSE; |
229 |
- const gchar *path = NULL; |
230 |
- |
231 |
- |
232 |
- ret_kh_startup = _kh_startup (); |
233 |
- g_assert (ret_kh_startup); |
234 |
+ kqueue_sub *sub; |
235 |
+ const gchar *path; |
236 |
|
237 |
path = filename; |
238 |
- if (!path) |
239 |
+ if (path == NULL) |
240 |
path = dirname; |
241 |
|
242 |
+#ifndef O_EVTONLY |
243 |
+ if (_ke_is_excluded (path)) |
244 |
+ { |
245 |
+ GFile *file = g_file_new_for_path (path); |
246 |
+ kqueue_monitor->fbfile = file; |
247 |
+ kqueue_monitor->fallback = _g_poll_file_monitor_new (file); |
248 |
+ g_signal_connect (kqueue_monitor->fallback, "changed", |
249 |
+ G_CALLBACK (_fallback_callback), kqueue_monitor); |
250 |
+ return; |
251 |
+ } |
252 |
+#endif |
253 |
+ |
254 |
/* For a directory monitor, create a subscription object anyway. |
255 |
* It will be used for directory diff calculation routines. |
256 |
* Wait, directory diff in a GKqueueFileMonitor? |
257 |
@@ -125,33 +191,13 @@ g_kqueue_file_monitor_start (GLocalFileM |
258 |
* file, GIO uses a GKqueueFileMonitor object for that. If a directory |
259 |
* will be created under that path, GKqueueFileMonitor will have to |
260 |
* handle the directory notifications. */ |
261 |
+ sub = _kqsub_new (path, local_monitor, source); |
262 |
+ if (sub == NULL) |
263 |
+ return; |
264 |
|
265 |
- sub = _kh_sub_new (path, TRUE, source); |
266 |
- |
267 |
- /* FIXME: what to do about errors here? we can't return NULL or another |
268 |
- * kind of error and an assertion is probably too hard (same issue as in |
269 |
- * the inotify backend) */ |
270 |
- g_assert (sub != NULL); |
271 |
kqueue_monitor->sub = sub; |
272 |
- |
273 |
- if (!_ke_is_excluded (path)) |
274 |
- _kh_add_sub (sub); |
275 |
- else |
276 |
- { |
277 |
- GFile *file = g_file_new_for_path (path); |
278 |
- kqueue_monitor->fbfile = file; |
279 |
- kqueue_monitor->fallback = _g_poll_file_monitor_new (file); |
280 |
- g_signal_connect (kqueue_monitor->fallback, |
281 |
- "changed", |
282 |
- G_CALLBACK (_fallback_callback), |
283 |
- kqueue_monitor); |
284 |
- } |
285 |
-} |
286 |
- |
287 |
-static gboolean |
288 |
-g_kqueue_file_monitor_is_supported (void) |
289 |
-{ |
290 |
- return _kh_startup (); |
291 |
+ if (!_kqsub_start_watching (sub)) |
292 |
+ _km_add_missing (sub); |
293 |
} |
294 |
|
295 |
static void |
296 |
@@ -175,24 +221,218 @@ g_kqueue_file_monitor_init (GKqueueFileM |
297 |
} |
298 |
|
299 |
static gboolean |
300 |
+g_kqueue_file_monitor_callback (gint fd, GIOCondition condition, gpointer user_data) |
301 |
+{ |
302 |
+ gint64 now = g_source_get_time (kq_source); |
303 |
+ kqueue_sub *sub; |
304 |
+ GFileMonitorSource *source; |
305 |
+ struct kevent ev; |
306 |
+ struct timespec ts; |
307 |
+ |
308 |
+ memset (&ts, 0, sizeof(ts)); |
309 |
+ while (kevent(fd, NULL, 0, &ev, 1, &ts) > 0) |
310 |
+ { |
311 |
+ GFileMonitorEvent mask = 0; |
312 |
+ |
313 |
+ if (ev.filter != EVFILT_VNODE || ev.udata == NULL) |
314 |
+ continue; |
315 |
+ |
316 |
+ sub = ev.udata; |
317 |
+ source = sub->source; |
318 |
+ |
319 |
+ if (ev.flags & EV_ERROR) |
320 |
+ ev.fflags = NOTE_REVOKE; |
321 |
+ |
322 |
+ if (ev.fflags & (NOTE_DELETE | NOTE_REVOKE)) |
323 |
+ { |
324 |
+ _kqsub_cancel (sub); |
325 |
+ _km_add_missing (sub); |
326 |
+ } |
327 |
+ |
328 |
+ if (sub->is_dir && ev.fflags & (NOTE_WRITE | NOTE_EXTEND)) |
329 |
+ { |
330 |
+ _kh_dir_diff (sub); |
331 |
+ ev.fflags &= ~(NOTE_WRITE | NOTE_EXTEND); |
332 |
+ } |
333 |
+ |
334 |
+ if (ev.fflags & NOTE_DELETE) |
335 |
+ { |
336 |
+ mask = G_FILE_MONITOR_EVENT_DELETED; |
337 |
+ } |
338 |
+ else if (ev.fflags & NOTE_ATTRIB) |
339 |
+ { |
340 |
+ mask = G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; |
341 |
+ } |
342 |
+ else if (ev.fflags & (NOTE_WRITE | NOTE_EXTEND)) |
343 |
+ { |
344 |
+ mask = G_FILE_MONITOR_EVENT_CHANGED; |
345 |
+ } |
346 |
+ else if (ev.fflags & NOTE_RENAME) |
347 |
+ { |
348 |
+ /* Since there’s apparently no way to get the new name of the |
349 |
+ * file out of kqueue(), all we can do is say that this one has |
350 |
+ * been deleted. */ |
351 |
+ mask = G_FILE_MONITOR_EVENT_DELETED; |
352 |
+ } |
353 |
+ else if (ev.fflags & NOTE_REVOKE) |
354 |
+ { |
355 |
+ mask = G_FILE_MONITOR_EVENT_UNMOUNTED; |
356 |
+ } |
357 |
+ |
358 |
+ if (mask) |
359 |
+ g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, now); |
360 |
+ } |
361 |
+ |
362 |
+ return TRUE; |
363 |
+} |
364 |
+ |
365 |
+static gboolean |
366 |
+g_kqueue_file_monitor_is_supported (void) |
367 |
+{ |
368 |
+ int errsv; |
369 |
+ |
370 |
+ G_LOCK (kq_lock); |
371 |
+ |
372 |
+ if (kq_queue == -1) |
373 |
+ { |
374 |
+ kq_queue = kqueue (); |
375 |
+ errsv = errno; |
376 |
+ |
377 |
+ if (kq_queue == -1) |
378 |
+ { |
379 |
+ g_warning ("Unable to create a kqueue: %s", g_strerror (errsv)); |
380 |
+ G_UNLOCK (kq_lock); |
381 |
+ return FALSE; |
382 |
+ } |
383 |
+ |
384 |
+ kq_source = g_unix_fd_source_new (kq_queue, G_IO_IN); |
385 |
+ g_source_set_callback (kq_source, (GSourceFunc) g_kqueue_file_monitor_callback, NULL, NULL); |
386 |
+ g_source_attach (kq_source, GLIB_PRIVATE_CALL (g_get_worker_context) ()); |
387 |
+ } |
388 |
+ |
389 |
+ G_UNLOCK (kq_lock); |
390 |
+ |
391 |
+ return TRUE; |
392 |
+} |
393 |
+ |
394 |
+static gboolean |
395 |
g_kqueue_file_monitor_cancel (GFileMonitor *monitor) |
396 |
{ |
397 |
GKqueueFileMonitor *kqueue_monitor = G_KQUEUE_FILE_MONITOR (monitor); |
398 |
|
399 |
if (kqueue_monitor->sub) |
400 |
{ |
401 |
- _kh_cancel_sub (kqueue_monitor->sub); |
402 |
- _kh_sub_free (kqueue_monitor->sub); |
403 |
+ _kqsub_cancel (kqueue_monitor->sub); |
404 |
+ _kqsub_free (kqueue_monitor->sub); |
405 |
kqueue_monitor->sub = NULL; |
406 |
} |
407 |
+#ifndef O_EVTONLY |
408 |
else if (kqueue_monitor->fallback) |
409 |
{ |
410 |
g_signal_handlers_disconnect_by_func (kqueue_monitor->fallback, _fallback_callback, kqueue_monitor); |
411 |
g_file_monitor_cancel (kqueue_monitor->fallback); |
412 |
} |
413 |
+#endif |
414 |
|
415 |
if (G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) |
416 |
(*G_FILE_MONITOR_CLASS (g_kqueue_file_monitor_parent_class)->cancel) (monitor); |
417 |
|
418 |
return TRUE; |
419 |
} |
420 |
+ |
421 |
+static kqueue_sub * |
422 |
+_kqsub_new (const gchar *filename, GLocalFileMonitor *mon, GFileMonitorSource *source) |
423 |
+{ |
424 |
+ kqueue_sub *sub; |
425 |
+ |
426 |
+ sub = g_slice_new (kqueue_sub); |
427 |
+ sub->filename = g_strdup (filename); |
428 |
+ sub->mon = mon; |
429 |
+ g_source_ref ((GSource *) source); |
430 |
+ sub->source = source; |
431 |
+ sub->fd = -1; |
432 |
+ sub->deps = NULL; |
433 |
+ sub->is_dir = 0; |
434 |
+ |
435 |
+ return sub; |
436 |
+} |
437 |
+ |
438 |
+static void |
439 |
+_kqsub_free (kqueue_sub *sub) |
440 |
+{ |
441 |
+ g_assert (sub->deps == NULL); |
442 |
+ g_assert (sub->fd == -1); |
443 |
+ |
444 |
+ g_source_unref ((GSource *) sub->source); |
445 |
+ g_free (sub->filename); |
446 |
+ g_slice_free (kqueue_sub, sub); |
447 |
+} |
448 |
+ |
449 |
+static gboolean |
450 |
+_kqsub_cancel (kqueue_sub *sub) |
451 |
+{ |
452 |
+ struct kevent ev; |
453 |
+ |
454 |
+ if (sub->deps) |
455 |
+ { |
456 |
+ dl_free (sub->deps); |
457 |
+ sub->deps = NULL; |
458 |
+ } |
459 |
+ |
460 |
+ _km_remove (sub); |
461 |
+ |
462 |
+ /* Only in the missing list? We're done! */ |
463 |
+ if (sub->fd == -1) |
464 |
+ return TRUE; |
465 |
+ |
466 |
+ EV_SET (&ev, sub->fd, EVFILT_VNODE, EV_DELETE, NOTE_ALL, 0, sub); |
467 |
+ if (kevent (kq_queue, &ev, 1, NULL, 0, NULL) == -1) |
468 |
+ { |
469 |
+ g_warning ("Unable to remove event for %s: %s", sub->filename, g_strerror (errno)); |
470 |
+ return FALSE; |
471 |
+ } |
472 |
+ |
473 |
+ close (sub->fd); |
474 |
+ sub->fd = -1; |
475 |
+ |
476 |
+ return TRUE; |
477 |
+} |
478 |
+ |
479 |
+gboolean |
480 |
+_kqsub_start_watching (kqueue_sub *sub) |
481 |
+{ |
482 |
+ struct stat st; |
483 |
+ struct kevent ev; |
484 |
+ |
485 |
+ sub->fd = open (sub->filename, O_KQFLAG); |
486 |
+ if (sub->fd == -1) |
487 |
+ return FALSE; |
488 |
+ |
489 |
+ if (fstat (sub->fd, &st) == -1) |
490 |
+ { |
491 |
+ g_warning ("fstat failed for %s: %s", sub->filename, g_strerror (errno)); |
492 |
+ close (sub->fd); |
493 |
+ sub->fd = -1; |
494 |
+ return FALSE; |
495 |
+ } |
496 |
+ |
497 |
+ sub->is_dir = (st.st_mode & S_IFDIR) ? 1 : 0; |
498 |
+ if (sub->is_dir) |
499 |
+ { |
500 |
+ if (sub->deps) |
501 |
+ dl_free (sub->deps); |
502 |
+ |
503 |
+ sub->deps = dl_listing (sub->filename); |
504 |
+ } |
505 |
+ |
506 |
+ EV_SET (&ev, sub->fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_ALL, 0, sub); |
507 |
+ if (kevent (kq_queue, &ev, 1, NULL, 0, NULL) == -1) |
508 |
+ { |
509 |
+ g_warning ("Unable to add event for %s: %s", sub->filename, g_strerror (errno)); |
510 |
+ close (sub->fd); |
511 |
+ sub->fd = -1; |
512 |
+ return FALSE; |
513 |
+ } |
514 |
+ |
515 |
+ return TRUE; |
516 |
+} |
517 |
--- gio/kqueue/kqueue-helper.c.orig 2016-10-22 05:18:22 UTC |
518 |
+++ gio/kqueue/kqueue-helper.c |
519 |
@@ -34,81 +34,6 @@ |
520 |
#include <errno.h> |
521 |
#include <pthread.h> |
522 |
#include "kqueue-helper.h" |
523 |
-#include "kqueue-utils.h" |
524 |
-#include "kqueue-thread.h" |
525 |
-#include "kqueue-missing.h" |
526 |
-#include "kqueue-exclusions.h" |
527 |
- |
528 |
-static gboolean kh_debug_enabled = FALSE; |
529 |
-#define KH_W if (kh_debug_enabled) g_warning |
530 |
- |
531 |
-static GHashTable *subs_hash_table = NULL; |
532 |
-G_LOCK_DEFINE_STATIC (hash_lock); |
533 |
- |
534 |
-static int kqueue_descriptor = -1; |
535 |
-static int kqueue_socket_pair[] = {-1, -1}; |
536 |
-static pthread_t kqueue_thread; |
537 |
- |
538 |
- |
539 |
-void _kh_file_appeared_cb (kqueue_sub *sub); |
540 |
- |
541 |
-/** |
542 |
- * accessor function for kqueue_descriptor |
543 |
- **/ |
544 |
-int |
545 |
-get_kqueue_descriptor() |
546 |
-{ |
547 |
- return kqueue_descriptor; |
548 |
-} |
549 |
- |
550 |
-/** |
551 |
- * convert_kqueue_events_to_gio: |
552 |
- * @flags: a set of kqueue filter flags |
553 |
- * @done: a pointer to #gboolean indicating that the |
554 |
- * conversion has been done (out) |
555 |
- * |
556 |
- * Translates kqueue filter flags into GIO event flags. |
557 |
- * |
558 |
- * Returns: a #GFileMonitorEvent |
559 |
- **/ |
560 |
-static GFileMonitorEvent |
561 |
-convert_kqueue_events_to_gio (uint32_t flags, gboolean *done) |
562 |
-{ |
563 |
- g_assert (done != NULL); |
564 |
- *done = FALSE; |
565 |
- |
566 |
- /* TODO: The following notifications should be emulated, if possible: |
567 |
- * - G_FILE_MONITOR_EVENT_PRE_UNMOUNT |
568 |
- */ |
569 |
- if (flags & NOTE_DELETE) |
570 |
- { |
571 |
- *done = TRUE; |
572 |
- return G_FILE_MONITOR_EVENT_DELETED; |
573 |
- } |
574 |
- if (flags & NOTE_ATTRIB) |
575 |
- { |
576 |
- *done = TRUE; |
577 |
- return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; |
578 |
- } |
579 |
- if (flags & (NOTE_WRITE | NOTE_EXTEND)) |
580 |
- { |
581 |
- *done = TRUE; |
582 |
- return G_FILE_MONITOR_EVENT_CHANGED; |
583 |
- } |
584 |
- if (flags & NOTE_RENAME) |
585 |
- { |
586 |
- *done = TRUE; |
587 |
- return G_FILE_MONITOR_EVENT_MOVED; |
588 |
- } |
589 |
- if (flags & NOTE_REVOKE) |
590 |
- { |
591 |
- *done = TRUE; |
592 |
- return G_FILE_MONITOR_EVENT_UNMOUNTED; |
593 |
- } |
594 |
- |
595 |
- /* done is FALSE */ |
596 |
- return 0; |
597 |
-} |
598 |
|
599 |
typedef struct { |
600 |
kqueue_sub *sub; |
601 |
@@ -236,323 +161,21 @@ static const traverse_cbs cbs = { |
602 |
|
603 |
|
604 |
void |
605 |
-_kh_dir_diff (kqueue_sub *sub, GFileMonitorSource *source) |
606 |
+_kh_dir_diff (kqueue_sub *sub) |
607 |
{ |
608 |
dep_list *was; |
609 |
handle_ctx ctx; |
610 |
|
611 |
- g_assert (sub != NULL); |
612 |
- g_assert (source != NULL); |
613 |
- |
614 |
memset (&ctx, 0, sizeof (handle_ctx)); |
615 |
ctx.sub = sub; |
616 |
- ctx.source = source; |
617 |
+ ctx.source = sub->source; |
618 |
|
619 |
was = sub->deps; |
620 |
sub->deps = dl_listing (sub->filename); |
621 |
- |
622 |
+ |
623 |
dl_calculate (was, sub->deps, &cbs, &ctx); |
624 |
|
625 |
dl_free (was); |
626 |
} |
627 |
|
628 |
|
629 |
-/** |
630 |
- * process_kqueue_notifications: |
631 |
- * @gioc: unused. |
632 |
- * @cond: unused. |
633 |
- * @data: unused. |
634 |
- * |
635 |
- * Processes notifications, coming from the kqueue thread. |
636 |
- * |
637 |
- * Reads notifications from the command file descriptor, emits the |
638 |
- * "changed" event on the appropriate monitor. |
639 |
- * |
640 |
- * A typical GIO Channel callback function. |
641 |
- * |
642 |
- * Returns: %TRUE |
643 |
- **/ |
644 |
-static gboolean |
645 |
-process_kqueue_notifications (GIOChannel *gioc, |
646 |
- GIOCondition cond, |
647 |
- gpointer data) |
648 |
-{ |
649 |
- struct kqueue_notification n; |
650 |
- kqueue_sub *sub = NULL; |
651 |
- GFileMonitorSource *source = NULL; |
652 |
- GFileMonitorEvent mask = 0; |
653 |
- |
654 |
- g_assert (kqueue_socket_pair[0] != -1); |
655 |
- if (!_ku_read (kqueue_socket_pair[0], &n, sizeof (struct kqueue_notification))) |
656 |
- { |
657 |
- KH_W ("Failed to read a kqueue notification, error %d", errno); |
658 |
- return TRUE; |
659 |
- } |
660 |
- |
661 |
- G_LOCK (hash_lock); |
662 |
- sub = (kqueue_sub *) g_hash_table_lookup (subs_hash_table, GINT_TO_POINTER (n.fd)); |
663 |
- G_UNLOCK (hash_lock); |
664 |
- |
665 |
- if (sub == NULL) |
666 |
- { |
667 |
- KH_W ("Got a notification for a deleted or non-existing subscription %d", |
668 |
- n.fd); |
669 |
- return TRUE; |
670 |
- } |
671 |
- |
672 |
- source = sub->user_data; |
673 |
- g_assert (source != NULL); |
674 |
- |
675 |
- if (n.flags & (NOTE_DELETE | NOTE_REVOKE)) |
676 |
- { |
677 |
- if (sub->deps) |
678 |
- { |
679 |
- dl_free (sub->deps); |
680 |
- sub->deps = NULL; |
681 |
- } |
682 |
- _km_add_missing (sub); |
683 |
- |
684 |
- if (!(n.flags & NOTE_REVOKE)) |
685 |
- { |
686 |
- /* Note that NOTE_REVOKE is issued by the kqueue thread |
687 |
- * on EV_ERROR kevent. In this case, a file descriptor is |
688 |
- * already closed from the kqueue thread, no need to close |
689 |
- * it manually */ |
690 |
- _kh_cancel_sub (sub); |
691 |
- } |
692 |
- } |
693 |
- |
694 |
- if (sub->is_dir && n.flags & (NOTE_WRITE | NOTE_EXTEND)) |
695 |
- { |
696 |
- _kh_dir_diff (sub, source); |
697 |
- n.flags &= ~(NOTE_WRITE | NOTE_EXTEND); |
698 |
- } |
699 |
- |
700 |
- if (n.flags) |
701 |
- { |
702 |
- gboolean done = FALSE; |
703 |
- mask = convert_kqueue_events_to_gio (n.flags, &done); |
704 |
- if (done == TRUE) |
705 |
- g_file_monitor_source_handle_event (source, mask, NULL, NULL, NULL, g_get_monotonic_time ()); |
706 |
- } |
707 |
- |
708 |
- return TRUE; |
709 |
-} |
710 |
- |
711 |
- |
712 |
-/* |
713 |
- * _kh_startup_impl: |
714 |
- * @unused: unused |
715 |
- * |
716 |
- * Kqueue backend startup code. Should be called only once. |
717 |
- * |
718 |
- * Returns: %TRUE on success, %FALSE otherwise. |
719 |
- **/ |
720 |
-static gpointer |
721 |
-_kh_startup_impl (gpointer unused) |
722 |
-{ |
723 |
- GIOChannel *channel = NULL; |
724 |
- gboolean result = FALSE; |
725 |
- |
726 |
- kqueue_descriptor = kqueue (); |
727 |
- result = (kqueue_descriptor != -1); |
728 |
- if (!result) |
729 |
- { |
730 |
- KH_W ("Failed to initialize kqueue\n!"); |
731 |
- return GINT_TO_POINTER (FALSE); |
732 |
- } |
733 |
- |
734 |
- result = socketpair (AF_UNIX, SOCK_STREAM, 0, kqueue_socket_pair); |
735 |
- if (result != 0) |
736 |
- { |
737 |
- KH_W ("Failed to create socket pair\n!"); |
738 |
- return GINT_TO_POINTER (FALSE) ; |
739 |
- } |
740 |
- |
741 |
- result = pthread_create (&kqueue_thread, |
742 |
- NULL, |
743 |
- _kqueue_thread_func, |
744 |
- &kqueue_socket_pair[1]); |
745 |
- if (result != 0) |
746 |
- { |
747 |
- KH_W ("Failed to run kqueue thread\n!"); |
748 |
- return GINT_TO_POINTER (FALSE); |
749 |
- } |
750 |
- |
751 |
- _km_init (_kh_file_appeared_cb); |
752 |
- |
753 |
- channel = g_io_channel_unix_new (kqueue_socket_pair[0]); |
754 |
- g_io_add_watch (channel, G_IO_IN, process_kqueue_notifications, NULL); |
755 |
- |
756 |
- subs_hash_table = g_hash_table_new (g_direct_hash, g_direct_equal); |
757 |
- |
758 |
- KH_W ("started gio kqueue backend\n"); |
759 |
- return GINT_TO_POINTER (TRUE); |
760 |
-} |
761 |
- |
762 |
- |
763 |
-/* |
764 |
- * _kh_startup: |
765 |
- * Kqueue backend initialization. |
766 |
- * |
767 |
- * Returns: %TRUE on success, %FALSE otherwise. |
768 |
- **/ |
769 |
-gboolean |
770 |
-_kh_startup (void) |
771 |
-{ |
772 |
- static GOnce init_once = G_ONCE_INIT; |
773 |
- g_once (&init_once, _kh_startup_impl, NULL); |
774 |
- return GPOINTER_TO_INT (init_once.retval); |
775 |
-} |
776 |
- |
777 |
- |
778 |
-/** |
779 |
- * _kh_start_watching: |
780 |
- * @sub: a #kqueue_sub |
781 |
- * |
782 |
- * Starts watching on a subscription. |
783 |
- * |
784 |
- * Returns: %TRUE on success, %FALSE otherwise. |
785 |
- **/ |
786 |
-gboolean |
787 |
-_kh_start_watching (kqueue_sub *sub) |
788 |
-{ |
789 |
- g_assert (kqueue_socket_pair[0] != -1); |
790 |
- g_assert (sub != NULL); |
791 |
- g_assert (sub->filename != NULL); |
792 |
- |
793 |
- /* kqueue requires a file descriptor to monitor. Sad but true */ |
794 |
-#if defined (O_EVTONLY) |
795 |
- sub->fd = open (sub->filename, O_EVTONLY); |
796 |
-#else |
797 |
- sub->fd = open (sub->filename, O_RDONLY); |
798 |
-#endif |
799 |
- |
800 |
- if (sub->fd == -1) |
801 |
- { |
802 |
- KH_W ("failed to open file %s (error %d)", sub->filename, errno); |
803 |
- return FALSE; |
804 |
- } |
805 |
- |
806 |
- _ku_file_information (sub->fd, &sub->is_dir, NULL); |
807 |
- if (sub->is_dir) |
808 |
- { |
809 |
- /* I know, it is very bad to make such decisions in this way and here. |
810 |
- * We already do have an user_data at the #kqueue_sub, and it may point to |
811 |
- * GKqueueFileMonitor or GKqueueDirectoryMonitor. For a directory case, |
812 |
- * we need to scan in contents for the further diffs. Ideally this process |
813 |
- * should be delegated to the GKqueueDirectoryMonitor, but for now I will |
814 |
- * do it in a dirty way right here. */ |
815 |
- if (sub->deps) |
816 |
- dl_free (sub->deps); |
817 |
- |
818 |
- sub->deps = dl_listing (sub->filename); |
819 |
- } |
820 |
- |
821 |
- G_LOCK (hash_lock); |
822 |
- g_hash_table_insert (subs_hash_table, GINT_TO_POINTER (sub->fd), sub); |
823 |
- G_UNLOCK (hash_lock); |
824 |
- |
825 |
- _kqueue_thread_push_fd (sub->fd); |
826 |
- |
827 |
- /* Bump the kqueue thread. It will pick up a new sub entry to monitor */ |
828 |
- if (!_ku_write (kqueue_socket_pair[0], "A", 1)) |
829 |
- KH_W ("Failed to bump the kqueue thread (add fd, error %d)", errno); |
830 |
- return TRUE; |
831 |
-} |
832 |
- |
833 |
- |
834 |
-/** |
835 |
- * _kh_add_sub: |
836 |
- * @sub: a #kqueue_sub |
837 |
- * |
838 |
- * Adds a subscription for monitoring. |
839 |
- * |
840 |
- * This funciton tries to start watching a subscription with |
841 |
- * _kh_start_watching(). On failure, i.e. when a file does not exist yet, |
842 |
- * the subscription will be added to a list of missing files to continue |
843 |
- * watching when the file will appear. |
844 |
- * |
845 |
- * Returns: %TRUE |
846 |
- **/ |
847 |
-gboolean |
848 |
-_kh_add_sub (kqueue_sub *sub) |
849 |
-{ |
850 |
- g_assert (sub != NULL); |
851 |
- |
852 |
- if (!_kh_start_watching (sub)) |
853 |
- _km_add_missing (sub); |
854 |
- |
855 |
- return TRUE; |
856 |
-} |
857 |
- |
858 |
- |
859 |
-/** |
860 |
- * _kh_cancel_sub: |
861 |
- * @sub a #kqueue_sub |
862 |
- * |
863 |
- * Stops monitoring on a subscription. |
864 |
- * |
865 |
- * Returns: %TRUE |
866 |
- **/ |
867 |
-gboolean |
868 |
-_kh_cancel_sub (kqueue_sub *sub) |
869 |
-{ |
870 |
- gboolean missing = FALSE; |
871 |
- g_assert (kqueue_socket_pair[0] != -1); |
872 |
- g_assert (sub != NULL); |
873 |
- |
874 |
- G_LOCK (hash_lock); |
875 |
- missing = !g_hash_table_remove (subs_hash_table, GINT_TO_POINTER (sub->fd)); |
876 |
- G_UNLOCK (hash_lock); |
877 |
- |
878 |
- if (missing) |
879 |
- { |
880 |
- /* If there were no fd for this subscription, file is still |
881 |
- * missing. */ |
882 |
- KH_W ("Removing subscription from missing"); |
883 |
- _km_remove (sub); |
884 |
- } |
885 |
- else |
886 |
- { |
887 |
- /* fd will be closed in the kqueue thread */ |
888 |
- _kqueue_thread_remove_fd (sub->fd); |
889 |
- |
890 |
- /* Bump the kqueue thread. It will pick up a new sub entry to remove*/ |
891 |
- if (!_ku_write (kqueue_socket_pair[0], "R", 1)) |
892 |
- KH_W ("Failed to bump the kqueue thread (remove fd, error %d)", errno); |
893 |
- } |
894 |
- |
895 |
- return TRUE; |
896 |
-} |
897 |
- |
898 |
- |
899 |
-/** |
900 |
- * _kh_file_appeared_cb: |
901 |
- * @sub: a #kqueue_sub |
902 |
- * |
903 |
- * A callback function for kqueue-missing subsystem. |
904 |
- * |
905 |
- * Signals that a missing file has finally appeared in the filesystem. |
906 |
- * Emits %G_FILE_MONITOR_EVENT_CREATED. |
907 |
- **/ |
908 |
-void |
909 |
-_kh_file_appeared_cb (kqueue_sub *sub) |
910 |
-{ |
911 |
- GFile* child; |
912 |
- |
913 |
- g_assert (sub != NULL); |
914 |
- g_assert (sub->filename); |
915 |
- |
916 |
- if (!g_file_test (sub->filename, G_FILE_TEST_EXISTS)) |
917 |
- return; |
918 |
- |
919 |
- child = g_file_new_for_path (sub->filename); |
920 |
- |
921 |
- g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), |
922 |
- child, |
923 |
- NULL, |
924 |
- G_FILE_MONITOR_EVENT_CREATED); |
925 |
- |
926 |
- g_object_unref (child); |
927 |
-} |
928 |
--- gio/kqueue/kqueue-helper.h.orig 2016-10-22 05:18:22 UTC |
929 |
+++ gio/kqueue/kqueue-helper.h |
930 |
@@ -23,16 +23,31 @@ |
931 |
#ifndef __KQUEUE_HELPER_H |
932 |
#define __KQUEUE_HELPER_H |
933 |
|
934 |
-#include "kqueue-sub.h" |
935 |
#include <gio/glocalfilemonitor.h> |
936 |
#include <gio/gfilemonitor.h> |
937 |
|
938 |
-gboolean _kh_startup (void); |
939 |
-gboolean _kh_add_sub (kqueue_sub *sub); |
940 |
-gboolean _kh_cancel_sub (kqueue_sub *sub); |
941 |
+#include "dep-list.h" |
942 |
|
943 |
-gboolean _kh_start_watching (kqueue_sub *sub); |
944 |
+/** |
945 |
+ * kqueue_sub: |
946 |
+ * @filename: a name of the file to monitor |
947 |
+ * @fd: the associated file descriptor (used by kqueue) |
948 |
+ * |
949 |
+ * Represents a subscription on a file or directory. |
950 |
+ */ |
951 |
+typedef struct |
952 |
+{ |
953 |
+ GLocalFileMonitor *mon; |
954 |
+ GFileMonitorSource *source; |
955 |
+ gchar* filename; |
956 |
+ int fd; |
957 |
+ dep_list* deps; |
958 |
+ int is_dir; |
959 |
+} kqueue_sub; |
960 |
|
961 |
-void _kh_dir_diff (kqueue_sub *sub, GFileMonitorSource *source); |
962 |
+gboolean _kqsub_start_watching (kqueue_sub *sub); |
963 |
+void _kh_dir_diff (kqueue_sub *sub); |
964 |
+void _km_add_missing (kqueue_sub *sub); |
965 |
+void _km_remove (kqueue_sub *sub); |
966 |
|
967 |
#endif /* __KQUEUE_HELPER_H */ |
968 |
--- gio/kqueue/kqueue-missing.c.orig 2016-10-22 05:18:22 UTC |
969 |
+++ gio/kqueue/kqueue-missing.c |
970 |
@@ -23,12 +23,12 @@ |
971 |
#include <glib.h> |
972 |
|
973 |
#include "kqueue-helper.h" |
974 |
-#include "kqueue-sub.h" |
975 |
-#include "kqueue-missing.h" |
976 |
|
977 |
|
978 |
#define SCAN_MISSING_TIME 4 /* 1/4 Hz */ |
979 |
|
980 |
+void _kh_file_appeared_cb (kqueue_sub *sub); |
981 |
+ |
982 |
static gboolean km_scan_missing (gpointer user_data); |
983 |
|
984 |
static gboolean km_debug_enabled = FALSE; |
985 |
@@ -38,21 +38,6 @@ static GSList *missing_subs_list = NULL; |
986 |
G_LOCK_DEFINE_STATIC (missing_lock); |
987 |
|
988 |
static volatile gboolean scan_missing_running = FALSE; |
989 |
-static on_create_cb file_appeared_callback; |
990 |
- |
991 |
- |
992 |
-/** |
993 |
- * _km_init: |
994 |
- * @cb: a callback function. It will be called when a watched file |
995 |
- * will appear. |
996 |
- * |
997 |
- * Initialize the kqueue-missing module (optional). |
998 |
- **/ |
999 |
-void |
1000 |
-_km_init (on_create_cb cb) |
1001 |
-{ |
1002 |
- file_appeared_callback = cb; |
1003 |
-} |
1004 |
|
1005 |
|
1006 |
/** |
1007 |
@@ -83,6 +68,35 @@ _km_add_missing (kqueue_sub *sub) |
1008 |
} |
1009 |
} |
1010 |
|
1011 |
+/** |
1012 |
+ * _kh_file_appeared_cb: |
1013 |
+ * @sub: a #kqueue_sub |
1014 |
+ * |
1015 |
+ * A callback function for kqueue-missing subsystem. |
1016 |
+ * |
1017 |
+ * Signals that a missing file has finally appeared in the filesystem. |
1018 |
+ * Emits %G_FILE_MONITOR_EVENT_CREATED. |
1019 |
+ **/ |
1020 |
+void |
1021 |
+_kh_file_appeared_cb (kqueue_sub *sub) |
1022 |
+{ |
1023 |
+ GFile *child; |
1024 |
+ |
1025 |
+ g_assert (sub != NULL); |
1026 |
+ g_assert (sub->filename); |
1027 |
+ |
1028 |
+ if (!g_file_test (sub->filename, G_FILE_TEST_EXISTS)) |
1029 |
+ return; |
1030 |
+ |
1031 |
+ child = g_file_new_for_path (sub->filename); |
1032 |
+ |
1033 |
+ g_file_monitor_emit_event (G_FILE_MONITOR (sub->mon), |
1034 |
+ child, |
1035 |
+ NULL, |
1036 |
+ G_FILE_MONITOR_EVENT_CREATED); |
1037 |
+ |
1038 |
+ g_object_unref (child); |
1039 |
+} |
1040 |
|
1041 |
/** |
1042 |
* km_scan_missing: |
1043 |
@@ -114,11 +128,10 @@ km_scan_missing (gpointer user_data) |
1044 |
g_assert (sub != NULL); |
1045 |
g_assert (sub->filename != NULL); |
1046 |
|
1047 |
- if (_kh_start_watching (sub)) |
1048 |
+ if (_kqsub_start_watching (sub)) |
1049 |
{ |
1050 |
KM_W ("file %s now exists, starting watching", sub->filename); |
1051 |
- if (file_appeared_callback) |
1052 |
- file_appeared_callback (sub); |
1053 |
+ _kh_file_appeared_cb (sub); |
1054 |
not_missing = g_slist_prepend (not_missing, head); |
1055 |
} |
1056 |
} |