/src/glib/gio/inotify/inotify-path.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */ |
2 | | |
3 | | /* inotify-path.c - GVFS Monitor based on inotify. |
4 | | |
5 | | Copyright (C) 2006 John McCutchan |
6 | | Copyright (C) 2009 Codethink Limited |
7 | | |
8 | | This library is free software; you can redistribute it and/or |
9 | | modify it under the terms of the GNU Lesser General Public |
10 | | License as published by the Free Software Foundation; either |
11 | | version 2.1 of the License, or (at your option) any later version. |
12 | | |
13 | | This library is distributed in the hope that it will be useful, |
14 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | | Lesser General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU Lesser General Public License |
19 | | along with this library; if not, see <http://www.gnu.org/licenses/>. |
20 | | |
21 | | Authors: |
22 | | John McCutchan <john@johnmccutchan.com> |
23 | | Ryan Lortie <desrt@desrt.ca> |
24 | | */ |
25 | | |
26 | | #include "config.h" |
27 | | |
28 | | /* Don't put conflicting kernel types in the global namespace: */ |
29 | | #define __KERNEL_STRICT_NAMES |
30 | | |
31 | | #include <sys/inotify.h> |
32 | | #include <string.h> |
33 | | #include <glib.h> |
34 | | #include "inotify-kernel.h" |
35 | | #include "inotify-path.h" |
36 | | #include "inotify-missing.h" |
37 | | |
38 | 0 | #define IP_INOTIFY_DIR_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF|IN_CLOSE_WRITE) |
39 | | |
40 | 0 | #define IP_INOTIFY_FILE_MASK (IN_MODIFY|IN_ATTRIB|IN_CLOSE_WRITE) |
41 | | |
42 | | /* Older libcs don't have this */ |
43 | | #ifndef IN_ONLYDIR |
44 | | #define IN_ONLYDIR 0 |
45 | | #endif |
46 | | |
47 | | typedef struct ip_watched_file_s { |
48 | | gchar *filename; |
49 | | gchar *path; |
50 | | gint32 wd; |
51 | | |
52 | | GList *subs; |
53 | | } ip_watched_file_t; |
54 | | |
55 | | typedef struct ip_watched_dir_s { |
56 | | char *path; |
57 | | /* TODO: We need to maintain a tree of watched directories |
58 | | * so that we can deliver move/delete events to sub folders. |
59 | | * Or the application could do it... |
60 | | */ |
61 | | struct ip_watched_dir_s* parent; |
62 | | GList* children; |
63 | | |
64 | | /* basename -> ip_watched_file_t |
65 | | * Maps basename to a ip_watched_file_t if the file is currently |
66 | | * being directly watched for changes (ie: 'hardlinks' mode). |
67 | | */ |
68 | | GHashTable *files_hash; |
69 | | |
70 | | /* Inotify state */ |
71 | | gint32 wd; |
72 | | |
73 | | /* List of inotify subscriptions */ |
74 | | GList *subs; |
75 | | } ip_watched_dir_t; |
76 | | |
77 | | static gboolean ip_debug_enabled = FALSE; |
78 | 0 | #define IP_W if (ip_debug_enabled) g_warning |
79 | | |
80 | | /* path -> ip_watched_dir */ |
81 | | static GHashTable * path_dir_hash = NULL; |
82 | | /* inotify_sub * -> ip_watched_dir * |
83 | | * |
84 | | * Each subscription is attached to a watched directory or it is on |
85 | | * the missing list |
86 | | */ |
87 | | static GHashTable * sub_dir_hash = NULL; |
88 | | /* This hash holds GLists of ip_watched_dir_t *'s |
89 | | * We need to hold a list because symbolic links can share |
90 | | * the same wd |
91 | | */ |
92 | | static GHashTable * wd_dir_hash = NULL; |
93 | | /* This hash holds GLists of ip_watched_file_t *'s |
94 | | * We need to hold a list because links can share |
95 | | * the same wd |
96 | | */ |
97 | | static GHashTable * wd_file_hash = NULL; |
98 | | |
99 | | static ip_watched_dir_t *ip_watched_dir_new (const char *path, |
100 | | int wd); |
101 | | static void ip_watched_dir_free (ip_watched_dir_t *dir); |
102 | | static gboolean ip_event_callback (ik_event_t *event); |
103 | | |
104 | | |
105 | | static gboolean (*event_callback)(ik_event_t *event, inotify_sub *sub, gboolean file_event); |
106 | | |
107 | | gboolean |
108 | | _ip_startup (gboolean (*cb)(ik_event_t *event, inotify_sub *sub, gboolean file_event)) |
109 | 0 | { |
110 | 0 | static gboolean initialized = FALSE; |
111 | 0 | static gboolean result = FALSE; |
112 | | |
113 | 0 | if (initialized == TRUE) |
114 | 0 | return result; |
115 | | |
116 | 0 | event_callback = cb; |
117 | 0 | result = _ik_startup (ip_event_callback); |
118 | |
|
119 | 0 | if (!result) |
120 | 0 | return FALSE; |
121 | | |
122 | 0 | path_dir_hash = g_hash_table_new (g_str_hash, g_str_equal); |
123 | 0 | sub_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal); |
124 | 0 | wd_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal); |
125 | 0 | wd_file_hash = g_hash_table_new (g_direct_hash, g_direct_equal); |
126 | | |
127 | 0 | initialized = TRUE; |
128 | 0 | return TRUE; |
129 | 0 | } |
130 | | |
131 | | static void |
132 | | ip_map_path_dir (const char *path, |
133 | | ip_watched_dir_t *dir) |
134 | 0 | { |
135 | 0 | g_assert (path && dir); |
136 | 0 | g_hash_table_insert (path_dir_hash, dir->path, dir); |
137 | 0 | } |
138 | | |
139 | | static void |
140 | | ip_map_sub_dir (inotify_sub *sub, |
141 | | ip_watched_dir_t *dir) |
142 | 0 | { |
143 | | /* Associate subscription and directory */ |
144 | 0 | g_assert (dir && sub); |
145 | 0 | g_hash_table_insert (sub_dir_hash, sub, dir); |
146 | 0 | dir->subs = g_list_prepend (dir->subs, sub); |
147 | 0 | } |
148 | | |
149 | | static void |
150 | | ip_map_wd_dir (gint32 wd, |
151 | | ip_watched_dir_t *dir) |
152 | 0 | { |
153 | 0 | GList *dir_list; |
154 | | |
155 | 0 | g_assert (wd >= 0 && dir); |
156 | 0 | dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd)); |
157 | 0 | dir_list = g_list_prepend (dir_list, dir); |
158 | 0 | g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list); |
159 | 0 | } |
160 | | |
161 | | static void |
162 | | ip_map_wd_file (gint32 wd, |
163 | | ip_watched_file_t *file) |
164 | 0 | { |
165 | 0 | GList *file_list; |
166 | |
|
167 | 0 | g_assert (wd >= 0 && file); |
168 | 0 | file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (wd)); |
169 | 0 | file_list = g_list_prepend (file_list, file); |
170 | 0 | g_hash_table_replace (wd_file_hash, GINT_TO_POINTER (wd), file_list); |
171 | 0 | } |
172 | | |
173 | | static void |
174 | | ip_unmap_wd_file (gint32 wd, |
175 | | ip_watched_file_t *file) |
176 | 0 | { |
177 | 0 | GList *file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (wd)); |
178 | |
|
179 | 0 | if (!file_list) |
180 | 0 | return; |
181 | | |
182 | 0 | g_assert (wd >= 0 && file); |
183 | 0 | file_list = g_list_remove (file_list, file); |
184 | 0 | if (file_list == NULL) |
185 | 0 | g_hash_table_remove (wd_file_hash, GINT_TO_POINTER (wd)); |
186 | 0 | else |
187 | 0 | g_hash_table_replace (wd_file_hash, GINT_TO_POINTER (wd), file_list); |
188 | 0 | } |
189 | | |
190 | | |
191 | | static ip_watched_file_t * |
192 | | ip_watched_file_new (const gchar *dirname, |
193 | | const gchar *filename) |
194 | 0 | { |
195 | 0 | ip_watched_file_t *file; |
196 | |
|
197 | 0 | file = g_new0 (ip_watched_file_t, 1); |
198 | 0 | file->path = g_strjoin ("/", dirname, filename, NULL); |
199 | 0 | file->filename = g_strdup (filename); |
200 | 0 | file->wd = -1; |
201 | |
|
202 | 0 | return file; |
203 | 0 | } |
204 | | |
205 | | static void |
206 | | ip_watched_file_free (ip_watched_file_t *file) |
207 | 0 | { |
208 | 0 | g_assert (file->subs == NULL); |
209 | 0 | g_free (file->filename); |
210 | 0 | g_free (file->path); |
211 | 0 | g_free (file); |
212 | 0 | } |
213 | | |
214 | | static void |
215 | | ip_watched_file_add_sub (ip_watched_file_t *file, |
216 | | inotify_sub *sub) |
217 | 0 | { |
218 | 0 | file->subs = g_list_prepend (file->subs, sub); |
219 | 0 | } |
220 | | |
221 | | static void |
222 | | ip_watched_file_start (ip_watched_file_t *file) |
223 | 0 | { |
224 | 0 | if (file->wd < 0) |
225 | 0 | { |
226 | 0 | gint err; |
227 | |
|
228 | 0 | file->wd = _ik_watch (file->path, |
229 | 0 | IP_INOTIFY_FILE_MASK, |
230 | 0 | &err); |
231 | |
|
232 | 0 | if (file->wd >= 0) |
233 | 0 | ip_map_wd_file (file->wd, file); |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | static void |
238 | | ip_watched_file_stop (ip_watched_file_t *file) |
239 | 0 | { |
240 | 0 | if (file->wd >= 0) |
241 | 0 | { |
242 | 0 | _ik_ignore (file->path, file->wd); |
243 | 0 | ip_unmap_wd_file (file->wd, file); |
244 | 0 | file->wd = -1; |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | gboolean |
249 | | _ip_start_watching (inotify_sub *sub) |
250 | 0 | { |
251 | 0 | gint32 wd; |
252 | 0 | int err; |
253 | 0 | ip_watched_dir_t *dir; |
254 | | |
255 | 0 | g_assert (sub); |
256 | 0 | g_assert (!sub->cancelled); |
257 | 0 | g_assert (sub->dirname); |
258 | | |
259 | 0 | IP_W ("Starting to watch %s\n", sub->dirname); |
260 | 0 | dir = g_hash_table_lookup (path_dir_hash, sub->dirname); |
261 | |
|
262 | 0 | if (dir == NULL) |
263 | 0 | { |
264 | 0 | IP_W ("Trying to add inotify watch "); |
265 | 0 | wd = _ik_watch (sub->dirname, IP_INOTIFY_DIR_MASK|IN_ONLYDIR, &err); |
266 | 0 | if (wd < 0) |
267 | 0 | { |
268 | 0 | IP_W ("Failed\n"); |
269 | 0 | return FALSE; |
270 | 0 | } |
271 | 0 | else |
272 | 0 | { |
273 | | /* Create new watched directory and associate it with the |
274 | | * wd hash and path hash |
275 | | */ |
276 | 0 | IP_W ("Success\n"); |
277 | 0 | dir = ip_watched_dir_new (sub->dirname, wd); |
278 | 0 | ip_map_wd_dir (wd, dir); |
279 | 0 | ip_map_path_dir (sub->dirname, dir); |
280 | 0 | } |
281 | 0 | } |
282 | 0 | else |
283 | 0 | IP_W ("Already watching\n"); |
284 | | |
285 | 0 | if (sub->hardlinks) |
286 | 0 | { |
287 | 0 | ip_watched_file_t *file; |
288 | |
|
289 | 0 | file = g_hash_table_lookup (dir->files_hash, sub->filename); |
290 | |
|
291 | 0 | if (file == NULL) |
292 | 0 | { |
293 | 0 | file = ip_watched_file_new (sub->dirname, sub->filename); |
294 | 0 | g_hash_table_insert (dir->files_hash, file->filename, file); |
295 | 0 | } |
296 | |
|
297 | 0 | ip_watched_file_add_sub (file, sub); |
298 | 0 | ip_watched_file_start (file); |
299 | 0 | } |
300 | |
|
301 | 0 | ip_map_sub_dir (sub, dir); |
302 | | |
303 | 0 | return TRUE; |
304 | 0 | } |
305 | | |
306 | | static void |
307 | | ip_unmap_path_dir (const char *path, |
308 | | ip_watched_dir_t *dir) |
309 | 0 | { |
310 | 0 | g_assert (path && dir); |
311 | 0 | g_hash_table_remove (path_dir_hash, dir->path); |
312 | 0 | } |
313 | | |
314 | | static void |
315 | | ip_unmap_wd_dir (gint32 wd, |
316 | | ip_watched_dir_t *dir) |
317 | 0 | { |
318 | 0 | GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd)); |
319 | | |
320 | 0 | if (!dir_list) |
321 | 0 | return; |
322 | | |
323 | 0 | g_assert (wd >= 0 && dir); |
324 | 0 | dir_list = g_list_remove (dir_list, dir); |
325 | 0 | if (dir_list == NULL) |
326 | 0 | g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER (dir->wd)); |
327 | 0 | else |
328 | 0 | g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list); |
329 | 0 | } |
330 | | |
331 | | static void |
332 | | ip_unmap_wd (gint32 wd) |
333 | 0 | { |
334 | 0 | GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd)); |
335 | 0 | if (!dir_list) |
336 | 0 | return; |
337 | 0 | g_assert (wd >= 0); |
338 | 0 | g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER (wd)); |
339 | 0 | g_list_free (dir_list); |
340 | 0 | } |
341 | | |
342 | | static void |
343 | | ip_unmap_sub_dir (inotify_sub *sub, |
344 | | ip_watched_dir_t *dir) |
345 | 0 | { |
346 | 0 | g_assert (sub && dir); |
347 | 0 | g_hash_table_remove (sub_dir_hash, sub); |
348 | 0 | dir->subs = g_list_remove (dir->subs, sub); |
349 | |
|
350 | 0 | if (sub->hardlinks) |
351 | 0 | { |
352 | 0 | ip_watched_file_t *file; |
353 | |
|
354 | 0 | file = g_hash_table_lookup (dir->files_hash, sub->filename); |
355 | 0 | file->subs = g_list_remove (file->subs, sub); |
356 | |
|
357 | 0 | if (file->subs == NULL) |
358 | 0 | { |
359 | 0 | g_hash_table_remove (dir->files_hash, sub->filename); |
360 | 0 | ip_watched_file_stop (file); |
361 | 0 | ip_watched_file_free (file); |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | static void |
367 | | ip_unmap_all_subs (ip_watched_dir_t *dir) |
368 | 0 | { |
369 | 0 | while (dir->subs != NULL) |
370 | 0 | ip_unmap_sub_dir (dir->subs->data, dir); |
371 | 0 | } |
372 | | |
373 | | gboolean |
374 | | _ip_stop_watching (inotify_sub *sub) |
375 | 0 | { |
376 | 0 | ip_watched_dir_t *dir = NULL; |
377 | | |
378 | 0 | dir = g_hash_table_lookup (sub_dir_hash, sub); |
379 | 0 | if (!dir) |
380 | 0 | return TRUE; |
381 | | |
382 | 0 | ip_unmap_sub_dir (sub, dir); |
383 | | |
384 | | /* No one is subscribing to this directory any more */ |
385 | 0 | if (dir->subs == NULL) |
386 | 0 | { |
387 | 0 | _ik_ignore (dir->path, dir->wd); |
388 | 0 | ip_unmap_wd_dir (dir->wd, dir); |
389 | 0 | ip_unmap_path_dir (dir->path, dir); |
390 | 0 | ip_watched_dir_free (dir); |
391 | 0 | } |
392 | | |
393 | 0 | return TRUE; |
394 | 0 | } |
395 | | |
396 | | |
397 | | static ip_watched_dir_t * |
398 | | ip_watched_dir_new (const char *path, |
399 | | gint32 wd) |
400 | 0 | { |
401 | 0 | ip_watched_dir_t *dir = g_new0 (ip_watched_dir_t, 1); |
402 | | |
403 | 0 | dir->path = g_strdup (path); |
404 | 0 | dir->files_hash = g_hash_table_new (g_str_hash, g_str_equal); |
405 | 0 | dir->wd = wd; |
406 | | |
407 | 0 | return dir; |
408 | 0 | } |
409 | | |
410 | | static void |
411 | | ip_watched_dir_free (ip_watched_dir_t *dir) |
412 | 0 | { |
413 | 0 | g_assert_cmpint (g_hash_table_size (dir->files_hash), ==, 0); |
414 | 0 | g_assert (dir->subs == NULL); |
415 | 0 | g_free (dir->path); |
416 | 0 | g_hash_table_unref (dir->files_hash); |
417 | 0 | g_free (dir); |
418 | 0 | } |
419 | | |
420 | | static void |
421 | | ip_wd_delete (gpointer data, |
422 | | gpointer user_data) |
423 | 0 | { |
424 | 0 | ip_watched_dir_t *dir = data; |
425 | 0 | GList *l = NULL; |
426 | | |
427 | 0 | for (l = dir->subs; l; l = l->next) |
428 | 0 | { |
429 | 0 | inotify_sub *sub = l->data; |
430 | | /* Add subscription to missing list */ |
431 | 0 | _im_add (sub); |
432 | 0 | } |
433 | 0 | ip_unmap_all_subs (dir); |
434 | | /* Unassociate the path and the directory */ |
435 | 0 | ip_unmap_path_dir (dir->path, dir); |
436 | 0 | ip_watched_dir_free (dir); |
437 | 0 | } |
438 | | |
439 | | static gboolean |
440 | | ip_event_dispatch (GList *dir_list, |
441 | | GList *file_list, |
442 | | ik_event_t *event) |
443 | 0 | { |
444 | 0 | gboolean interesting = FALSE; |
445 | |
|
446 | 0 | GList *l; |
447 | | |
448 | 0 | if (!event) |
449 | 0 | return FALSE; |
450 | | |
451 | 0 | for (l = dir_list; l; l = l->next) |
452 | 0 | { |
453 | 0 | GList *subl; |
454 | 0 | ip_watched_dir_t *dir = l->data; |
455 | | |
456 | 0 | for (subl = dir->subs; subl; subl = subl->next) |
457 | 0 | { |
458 | 0 | inotify_sub *sub = subl->data; |
459 | | |
460 | | /* If the subscription and the event |
461 | | * contain a filename and they don't |
462 | | * match, we don't deliver this event. |
463 | | */ |
464 | 0 | if (sub->filename && |
465 | 0 | event->name && |
466 | 0 | strcmp (sub->filename, event->name) && |
467 | 0 | (!event->pair || !event->pair->name || strcmp (sub->filename, event->pair->name))) |
468 | 0 | continue; |
469 | | |
470 | | /* If the subscription has a filename |
471 | | * but this event doesn't, we don't |
472 | | * deliver this event. |
473 | | */ |
474 | 0 | if (sub->filename && !event->name) |
475 | 0 | continue; |
476 | | |
477 | | /* If we're also watching the file directly |
478 | | * don't report events that will also be |
479 | | * reported on the file itself. |
480 | | */ |
481 | 0 | if (sub->hardlinks) |
482 | 0 | { |
483 | 0 | event->mask &= ~IP_INOTIFY_FILE_MASK; |
484 | 0 | if (!event->mask) |
485 | 0 | continue; |
486 | 0 | } |
487 | | |
488 | | /* FIXME: We might need to synthesize |
489 | | * DELETE/UNMOUNT events when |
490 | | * the filename doesn't match |
491 | | */ |
492 | | |
493 | 0 | interesting |= event_callback (event, sub, FALSE); |
494 | |
|
495 | 0 | if (sub->hardlinks) |
496 | 0 | { |
497 | 0 | ip_watched_file_t *file; |
498 | |
|
499 | 0 | file = g_hash_table_lookup (dir->files_hash, sub->filename); |
500 | |
|
501 | 0 | if (file != NULL) |
502 | 0 | { |
503 | 0 | if (event->mask & (IN_MOVED_FROM | IN_DELETE)) |
504 | 0 | ip_watched_file_stop (file); |
505 | |
|
506 | 0 | if (event->mask & (IN_MOVED_TO | IN_CREATE)) |
507 | 0 | ip_watched_file_start (file); |
508 | 0 | } |
509 | 0 | } |
510 | 0 | } |
511 | 0 | } |
512 | |
|
513 | 0 | for (l = file_list; l; l = l->next) |
514 | 0 | { |
515 | 0 | ip_watched_file_t *file = l->data; |
516 | 0 | GList *subl; |
517 | |
|
518 | 0 | for (subl = file->subs; subl; subl = subl->next) |
519 | 0 | { |
520 | 0 | inotify_sub *sub = subl->data; |
521 | |
|
522 | 0 | interesting |= event_callback (event, sub, TRUE); |
523 | 0 | } |
524 | 0 | } |
525 | |
|
526 | 0 | return interesting; |
527 | 0 | } |
528 | | |
529 | | static gboolean |
530 | | ip_event_callback (ik_event_t *event) |
531 | 0 | { |
532 | 0 | gboolean interesting = FALSE; |
533 | 0 | GList* dir_list = NULL; |
534 | 0 | GList *file_list = NULL; |
535 | | |
536 | | /* We can ignore the IGNORED events. Likewise, if the event queue overflowed, |
537 | | * there is not much we can do to recover. */ |
538 | 0 | if (event->mask & (IN_IGNORED | IN_Q_OVERFLOW)) |
539 | 0 | { |
540 | 0 | _ik_event_free (event); |
541 | 0 | return TRUE; |
542 | 0 | } |
543 | | |
544 | 0 | dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd)); |
545 | 0 | file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->wd)); |
546 | |
|
547 | 0 | if (event->mask & IP_INOTIFY_DIR_MASK) |
548 | 0 | interesting |= ip_event_dispatch (dir_list, file_list, event); |
549 | | |
550 | | /* Only deliver paired events if the wds are separate */ |
551 | 0 | if (event->pair && event->pair->wd != event->wd) |
552 | 0 | { |
553 | 0 | dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd)); |
554 | 0 | file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->pair->wd)); |
555 | |
|
556 | 0 | if (event->pair->mask & IP_INOTIFY_DIR_MASK) |
557 | 0 | interesting |= ip_event_dispatch (dir_list, file_list, event->pair); |
558 | 0 | } |
559 | | |
560 | | /* We have to manage the missing list |
561 | | * when we get an event that means the |
562 | | * file has been deleted/moved/unmounted. |
563 | | */ |
564 | 0 | if (event->mask & IN_DELETE_SELF || |
565 | 0 | event->mask & IN_MOVE_SELF || |
566 | 0 | event->mask & IN_UNMOUNT) |
567 | 0 | { |
568 | | /* Add all subscriptions to missing list */ |
569 | 0 | g_list_foreach (dir_list, ip_wd_delete, NULL); |
570 | | /* Unmap all directories attached to this wd */ |
571 | 0 | ip_unmap_wd (event->wd); |
572 | 0 | } |
573 | | |
574 | 0 | _ik_event_free (event); |
575 | |
|
576 | 0 | return interesting; |
577 | 0 | } |
578 | | |
579 | | const char * |
580 | | _ip_get_path_for_wd (gint32 wd) |
581 | 0 | { |
582 | 0 | GList *dir_list; |
583 | 0 | ip_watched_dir_t *dir; |
584 | |
|
585 | 0 | g_assert (wd >= 0); |
586 | 0 | dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd)); |
587 | 0 | if (dir_list) |
588 | 0 | { |
589 | 0 | dir = dir_list->data; |
590 | 0 | if (dir) |
591 | 0 | return dir->path; |
592 | 0 | } |
593 | | |
594 | 0 | return NULL; |
595 | 0 | } |