1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Alexander Larsson <alexl@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include "gioenumtypes.h"
24 #include "glocalfilemonitor.h"
25 #include "giomodule-priv.h"
26 #include "gioerror.h"
27 #include "glibintl.h"
28 #include "glocalfile.h"
29 #include "glib-private.h"
30 
31 #include <string.h>
32 
33 #define DEFAULT_RATE_LIMIT                           800 * G_TIME_SPAN_MILLISECOND
34 #define VIRTUAL_CHANGES_DONE_DELAY                     2 * G_TIME_SPAN_SECOND
35 
36 /* GFileMonitorSource is a GSource responsible for emitting the changed
37  * signals in the owner-context of the GFileMonitor.
38  *
39  * It contains functionality for cross-thread queuing of events.  It
40  * also handles merging of CHANGED events and emission of CHANGES_DONE
41  * events.
42  *
43  * We use the "priv" pointer in the external struct to store it.
44  */
45 struct _GFileMonitorSource {
46   GSource       source;
47 
48   GMutex        lock;
49   GWeakRef      instance_ref;
50   GFileMonitorFlags flags;
51   gchar        *dirname;
52   gchar        *basename;
53   gchar        *filename;
54   GSequence    *pending_changes; /* sorted by ready time */
55   GHashTable   *pending_changes_table;
56   GQueue        event_queue;
57   gint64        rate_limit;
58 };
59 
60 /* PendingChange is a struct to keep track of a file that needs to have
61  * (at least) a CHANGES_DONE_HINT event sent for it in the near future.
62  *
63  * If 'dirty' is TRUE then a CHANGED event also needs to be sent.
64  *
65  * last_emission is the last time a CHANGED event was emitted.  It is
66  * used to calculate the time to send the next event.
67  */
68 typedef struct {
69   gchar    *child;
70   guint64   last_emission : 63;
71   guint64   dirty         :  1;
72 } PendingChange;
73 
74 /* QueuedEvent is a signal that will be sent immediately, as soon as the
75  * source gets a chance to dispatch.  The existence of any queued event
76  * implies that the source is ready now.
77  */
78 typedef struct
79 {
80   GFileMonitorEvent event_type;
81   GFile *child;
82   GFile *other;
83 } QueuedEvent;
84 
85 static gint64
pending_change_get_ready_time(const PendingChange * change,GFileMonitorSource * fms)86 pending_change_get_ready_time (const PendingChange *change,
87                                GFileMonitorSource  *fms)
88 {
89   if (change->dirty)
90     return change->last_emission + fms->rate_limit;
91   else
92     return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY;
93 }
94 
95 static int
pending_change_compare_ready_time(gconstpointer a_p,gconstpointer b_p,gpointer user_data)96 pending_change_compare_ready_time (gconstpointer a_p,
97                                    gconstpointer b_p,
98                                    gpointer      user_data)
99 {
100   GFileMonitorSource *fms = user_data;
101   const PendingChange *a = a_p;
102   const PendingChange *b = b_p;
103   gint64 ready_time_a;
104   gint64 ready_time_b;
105 
106   ready_time_a = pending_change_get_ready_time (a, fms);
107   ready_time_b = pending_change_get_ready_time (b, fms);
108 
109   if (ready_time_a < ready_time_b)
110     return -1;
111   else
112     return ready_time_a > ready_time_b;
113 }
114 
115 static void
pending_change_free(gpointer data)116 pending_change_free (gpointer data)
117 {
118   PendingChange *change = data;
119 
120   g_free (change->child);
121 
122   g_slice_free (PendingChange, change);
123 }
124 
125 static void
queued_event_free(QueuedEvent * event)126 queued_event_free (QueuedEvent *event)
127 {
128   g_object_unref (event->child);
129   if (event->other)
130     g_object_unref (event->other);
131 
132   g_slice_free (QueuedEvent, event);
133 }
134 
135 static gint64
g_file_monitor_source_get_ready_time(GFileMonitorSource * fms)136 g_file_monitor_source_get_ready_time (GFileMonitorSource *fms)
137 {
138   GSequenceIter *iter;
139 
140   if (fms->event_queue.length)
141     return 0;
142 
143   iter = g_sequence_get_begin_iter (fms->pending_changes);
144   if (g_sequence_iter_is_end (iter))
145     return -1;
146 
147   return pending_change_get_ready_time (g_sequence_get (iter), fms);
148 }
149 
150 static void
g_file_monitor_source_update_ready_time(GFileMonitorSource * fms)151 g_file_monitor_source_update_ready_time (GFileMonitorSource *fms)
152 {
153   g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms));
154 }
155 
156 static GSequenceIter *
g_file_monitor_source_find_pending_change(GFileMonitorSource * fms,const gchar * child)157 g_file_monitor_source_find_pending_change (GFileMonitorSource *fms,
158                                            const gchar        *child)
159 {
160   return g_hash_table_lookup (fms->pending_changes_table, child);
161 }
162 
163 static void
g_file_monitor_source_add_pending_change(GFileMonitorSource * fms,const gchar * child,gint64 now)164 g_file_monitor_source_add_pending_change (GFileMonitorSource *fms,
165                                           const gchar        *child,
166                                           gint64              now)
167 {
168   PendingChange *change;
169   GSequenceIter *iter;
170 
171   change = g_slice_new (PendingChange);
172   change->child = g_strdup (child);
173   change->last_emission = now;
174   change->dirty = FALSE;
175 
176   iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms);
177   g_hash_table_insert (fms->pending_changes_table, change->child, iter);
178 }
179 
180 static gboolean
g_file_monitor_source_set_pending_change_dirty(GFileMonitorSource * fms,GSequenceIter * iter)181 g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms,
182                                                 GSequenceIter      *iter)
183 {
184   PendingChange *change;
185 
186   change = g_sequence_get (iter);
187 
188   /* if it was already dirty then this change is 'uninteresting' */
189   if (change->dirty)
190     return FALSE;
191 
192   change->dirty = TRUE;
193 
194   g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
195 
196   return TRUE;
197 }
198 
199 static gboolean
g_file_monitor_source_get_pending_change_dirty(GFileMonitorSource * fms,GSequenceIter * iter)200 g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms,
201                                                 GSequenceIter      *iter)
202 {
203   PendingChange *change;
204 
205   change = g_sequence_get (iter);
206 
207   return change->dirty;
208 }
209 
210 static void
g_file_monitor_source_remove_pending_change(GFileMonitorSource * fms,GSequenceIter * iter,const gchar * child)211 g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms,
212                                              GSequenceIter      *iter,
213                                              const gchar        *child)
214 {
215   /* must remove the hash entry first -- its key is owned by the data
216    * which will be freed when removing the sequence iter
217    */
218   g_hash_table_remove (fms->pending_changes_table, child);
219   g_sequence_remove (iter);
220 }
221 
222 static void
g_file_monitor_source_queue_event(GFileMonitorSource * fms,GFileMonitorEvent event_type,const gchar * child,GFile * other)223 g_file_monitor_source_queue_event (GFileMonitorSource *fms,
224                                    GFileMonitorEvent   event_type,
225                                    const gchar        *child,
226                                    GFile              *other)
227 {
228   QueuedEvent *event;
229 
230   event = g_slice_new (QueuedEvent);
231   event->event_type = event_type;
232   if (child != NULL && fms->dirname != NULL)
233     event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child);
234   else if (child != NULL)
235     {
236       gchar *dirname = g_path_get_dirname (fms->filename);
237       event->child = g_local_file_new_from_dirname_and_basename (dirname, child);
238       g_free (dirname);
239     }
240   else if (fms->dirname)
241     event->child = _g_local_file_new (fms->dirname);
242   else if (fms->filename)
243     event->child = _g_local_file_new (fms->filename);
244   event->other = other;
245   if (other)
246     g_object_ref (other);
247 
248   g_queue_push_tail (&fms->event_queue, event);
249 }
250 
251 static gboolean
g_file_monitor_source_file_changed(GFileMonitorSource * fms,const gchar * child,gint64 now)252 g_file_monitor_source_file_changed (GFileMonitorSource *fms,
253                                     const gchar        *child,
254                                     gint64              now)
255 {
256   GSequenceIter *pending;
257   gboolean interesting;
258 
259   pending = g_file_monitor_source_find_pending_change (fms, child);
260 
261   /* If there is no pending change, emit one and create a record,
262    * else: just mark the existing record as dirty.
263    */
264   if (!pending)
265     {
266       g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
267       g_file_monitor_source_add_pending_change (fms, child, now);
268       interesting = TRUE;
269     }
270   else
271     interesting = g_file_monitor_source_set_pending_change_dirty (fms, pending);
272 
273   g_file_monitor_source_update_ready_time (fms);
274 
275   return interesting;
276 }
277 
278 static void
g_file_monitor_source_file_changes_done(GFileMonitorSource * fms,const gchar * child)279 g_file_monitor_source_file_changes_done (GFileMonitorSource *fms,
280                                          const gchar        *child)
281 {
282   GSequenceIter *pending;
283 
284   pending = g_file_monitor_source_find_pending_change (fms, child);
285   if (pending)
286     {
287       /* If it is dirty, make sure we push out the last CHANGED event */
288       if (g_file_monitor_source_get_pending_change_dirty (fms, pending))
289         g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
290 
291       g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
292       g_file_monitor_source_remove_pending_change (fms, pending, child);
293     }
294 }
295 
296 static void
g_file_monitor_source_file_created(GFileMonitorSource * fms,const gchar * child,gint64 event_time)297 g_file_monitor_source_file_created (GFileMonitorSource *fms,
298                                     const gchar        *child,
299                                     gint64              event_time)
300 {
301   /* Unlikely, but if we have pending changes for this filename, make
302    * sure we flush those out first, before creating the new ones.
303    */
304   g_file_monitor_source_file_changes_done (fms, child);
305 
306   /* Emit CREATE and add a pending changes record */
307   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
308   g_file_monitor_source_add_pending_change (fms, child, event_time);
309 }
310 
311 static void
g_file_monitor_source_send_event(GFileMonitorSource * fms,GFileMonitorEvent event_type,const gchar * child,GFile * other)312 g_file_monitor_source_send_event (GFileMonitorSource *fms,
313                                   GFileMonitorEvent   event_type,
314                                   const gchar        *child,
315                                   GFile              *other)
316 {
317   /* always flush any pending changes before we queue a new event */
318   g_file_monitor_source_file_changes_done (fms, child);
319   g_file_monitor_source_queue_event (fms, event_type, child, other);
320 }
321 
322 static void
g_file_monitor_source_send_synthetic_created(GFileMonitorSource * fms,const gchar * child)323 g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms,
324                                               const gchar        *child)
325 {
326   g_file_monitor_source_file_changes_done (fms, child);
327   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
328   g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
329 }
330 
331 #ifndef G_DISABLE_ASSERT
332 static gboolean
is_basename(const gchar * name)333 is_basename (const gchar *name)
334 {
335   if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0'))
336     return FALSE;
337 
338   return !strchr (name, '/');
339 }
340 #endif  /* !G_DISABLE_ASSERT */
341 
342 gboolean
g_file_monitor_source_handle_event(GFileMonitorSource * fms,GFileMonitorEvent event_type,const gchar * child,const gchar * rename_to,GFile * other,gint64 event_time)343 g_file_monitor_source_handle_event (GFileMonitorSource *fms,
344                                     GFileMonitorEvent   event_type,
345                                     const gchar        *child,
346                                     const gchar        *rename_to,
347                                     GFile              *other,
348                                     gint64              event_time)
349 {
350   gboolean interesting = TRUE;
351   GFileMonitor *instance = NULL;
352 
353   g_assert (!child || is_basename (child));
354   g_assert (!rename_to || is_basename (rename_to));
355 
356   if (fms->basename && (!child || !g_str_equal (child, fms->basename))
357                     && (!rename_to || !g_str_equal (rename_to, fms->basename)))
358     return TRUE;
359 
360   g_mutex_lock (&fms->lock);
361 
362   /* monitor is already gone -- don't bother */
363   instance = g_weak_ref_get (&fms->instance_ref);
364   if (instance == NULL)
365     {
366       g_mutex_unlock (&fms->lock);
367       return TRUE;
368     }
369 
370   switch (event_type)
371     {
372     case G_FILE_MONITOR_EVENT_CREATED:
373       g_assert (!other && !rename_to);
374       g_file_monitor_source_file_created (fms, child, event_time);
375       break;
376 
377     case G_FILE_MONITOR_EVENT_CHANGED:
378       g_assert (!other && !rename_to);
379       interesting = g_file_monitor_source_file_changed (fms, child, event_time);
380       break;
381 
382     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
383       g_assert (!other && !rename_to);
384       g_file_monitor_source_file_changes_done (fms, child);
385       break;
386 
387     case G_FILE_MONITOR_EVENT_MOVED_IN:
388       g_assert (!rename_to);
389       if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
390         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other);
391       else
392         g_file_monitor_source_send_synthetic_created (fms, child);
393       break;
394 
395     case G_FILE_MONITOR_EVENT_MOVED_OUT:
396       g_assert (!rename_to);
397       if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
398         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other);
399       else if (other && (fms->flags & G_FILE_MONITOR_SEND_MOVED))
400         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other);
401       else
402         g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
403       break;
404 
405     case G_FILE_MONITOR_EVENT_RENAMED:
406       g_assert (!other && rename_to);
407       if (fms->flags & (G_FILE_MONITOR_WATCH_MOVES | G_FILE_MONITOR_SEND_MOVED))
408         {
409           GFile *other;
410           const gchar *dirname;
411           gchar *allocated_dirname = NULL;
412           GFileMonitorEvent event;
413 
414           event = (fms->flags & G_FILE_MONITOR_WATCH_MOVES) ? G_FILE_MONITOR_EVENT_RENAMED : G_FILE_MONITOR_EVENT_MOVED;
415 
416           if (fms->dirname != NULL)
417             dirname = fms->dirname;
418           else
419             {
420               allocated_dirname = g_path_get_dirname (fms->filename);
421               dirname = allocated_dirname;
422             }
423 
424           other = g_local_file_new_from_dirname_and_basename (dirname, rename_to);
425           g_file_monitor_source_file_changes_done (fms, rename_to);
426           g_file_monitor_source_send_event (fms, event, child, other);
427 
428           g_object_unref (other);
429           g_free (allocated_dirname);
430         }
431       else
432         {
433           g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
434           g_file_monitor_source_send_synthetic_created (fms, rename_to);
435         }
436       break;
437 
438     case G_FILE_MONITOR_EVENT_DELETED:
439     case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
440     case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
441     case G_FILE_MONITOR_EVENT_UNMOUNTED:
442       g_assert (!other && !rename_to);
443       g_file_monitor_source_send_event (fms, event_type, child, NULL);
444       break;
445 
446     case G_FILE_MONITOR_EVENT_MOVED:
447       /* was never available in this API */
448     default:
449       g_assert_not_reached ();
450     }
451 
452   g_file_monitor_source_update_ready_time (fms);
453 
454   g_mutex_unlock (&fms->lock);
455   g_clear_object (&instance);
456 
457   return interesting;
458 }
459 
460 static gint64
g_file_monitor_source_get_rate_limit(GFileMonitorSource * fms)461 g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms)
462 {
463   gint64 rate_limit;
464 
465   g_mutex_lock (&fms->lock);
466   rate_limit = fms->rate_limit;
467   g_mutex_unlock (&fms->lock);
468 
469   return rate_limit;
470 }
471 
472 static gboolean
g_file_monitor_source_set_rate_limit(GFileMonitorSource * fms,gint64 rate_limit)473 g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms,
474                                       gint64              rate_limit)
475 {
476   gboolean changed;
477 
478   g_mutex_lock (&fms->lock);
479 
480   if (rate_limit != fms->rate_limit)
481     {
482       fms->rate_limit = rate_limit;
483 
484       g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms);
485       g_file_monitor_source_update_ready_time (fms);
486 
487       changed = TRUE;
488     }
489   else
490     changed = FALSE;
491 
492   g_mutex_unlock (&fms->lock);
493 
494   return changed;
495 }
496 
497 static gboolean
g_file_monitor_source_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)498 g_file_monitor_source_dispatch (GSource     *source,
499                                 GSourceFunc  callback,
500                                 gpointer     user_data)
501 {
502   GFileMonitorSource *fms = (GFileMonitorSource *) source;
503   QueuedEvent *event;
504   GQueue event_queue;
505   gint64 now;
506   GFileMonitor *instance = NULL;
507 
508   /* make sure the monitor still exists */
509   instance = g_weak_ref_get (&fms->instance_ref);
510   if (instance == NULL)
511     return FALSE;
512 
513   now = g_source_get_time (source);
514 
515   /* Acquire the lock once and grab all events in one go, handling the
516    * queued events first.  This avoids strange possibilities in cases of
517    * long delays, such as CHANGED events coming before CREATED events.
518    *
519    * We do this by converting the applicable pending changes into queued
520    * events (after the ones already queued) and then stealing the entire
521    * event queue in one go.
522    */
523   g_mutex_lock (&fms->lock);
524 
525   /* Create events for any pending changes that are due to fire */
526   while (!g_sequence_is_empty (fms->pending_changes))
527     {
528       GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes);
529       PendingChange *pending = g_sequence_get (iter);
530 
531       /* We've gotten to a pending change that's not ready.  Stop. */
532       if (pending_change_get_ready_time (pending, fms) > now)
533         break;
534 
535       if (pending->dirty)
536         {
537           /* It's time to send another CHANGED and update the record */
538           g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL);
539           pending->last_emission = now;
540           pending->dirty = FALSE;
541 
542           g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
543         }
544       else
545         {
546           /* It's time to send CHANGES_DONE and remove the pending record */
547           g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL);
548           g_file_monitor_source_remove_pending_change (fms, iter, pending->child);
549         }
550     }
551 
552   /* Steal the queue */
553   memcpy (&event_queue, &fms->event_queue, sizeof event_queue);
554   memset (&fms->event_queue, 0, sizeof fms->event_queue);
555 
556   g_file_monitor_source_update_ready_time (fms);
557 
558   g_mutex_unlock (&fms->lock);
559   g_clear_object (&instance);
560 
561   /* We now have our list of events to deliver */
562   while ((event = g_queue_pop_head (&event_queue)))
563     {
564       /* an event handler could destroy 'instance', so check each time */
565       instance = g_weak_ref_get (&fms->instance_ref);
566       if (instance != NULL)
567         g_file_monitor_emit_event (instance, event->child, event->other, event->event_type);
568 
569       g_clear_object (&instance);
570       queued_event_free (event);
571     }
572 
573   return TRUE;
574 }
575 
576 static void
g_file_monitor_source_dispose(GFileMonitorSource * fms)577 g_file_monitor_source_dispose (GFileMonitorSource *fms)
578 {
579   GHashTableIter iter;
580   gpointer seqiter;
581   QueuedEvent *event;
582 
583   g_mutex_lock (&fms->lock);
584 
585   g_hash_table_iter_init (&iter, fms->pending_changes_table);
586   while (g_hash_table_iter_next (&iter, NULL, &seqiter))
587     {
588       g_hash_table_iter_remove (&iter);
589       g_sequence_remove (seqiter);
590     }
591 
592   while ((event = g_queue_pop_head (&fms->event_queue)))
593     queued_event_free (event);
594 
595   g_assert (g_sequence_is_empty (fms->pending_changes));
596   g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
597   g_assert (fms->event_queue.length == 0);
598   g_weak_ref_set (&fms->instance_ref, NULL);
599 
600   g_file_monitor_source_update_ready_time (fms);
601 
602   g_mutex_unlock (&fms->lock);
603 
604   g_source_destroy ((GSource *) fms);
605 }
606 
607 static void
g_file_monitor_source_finalize(GSource * source)608 g_file_monitor_source_finalize (GSource *source)
609 {
610   GFileMonitorSource *fms = (GFileMonitorSource *) source;
611 
612   /* should already have been cleared in dispose of the monitor */
613   g_assert (g_weak_ref_get (&fms->instance_ref) == NULL);
614   g_weak_ref_clear (&fms->instance_ref);
615 
616   g_assert (g_sequence_is_empty (fms->pending_changes));
617   g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
618   g_assert (fms->event_queue.length == 0);
619 
620   g_hash_table_unref (fms->pending_changes_table);
621   g_sequence_free (fms->pending_changes);
622 
623   g_free (fms->dirname);
624   g_free (fms->basename);
625   g_free (fms->filename);
626 
627   g_mutex_clear (&fms->lock);
628 }
629 
630 static guint
str_hash0(gconstpointer str)631 str_hash0 (gconstpointer str)
632 {
633   return str ? g_str_hash (str) : 0;
634 }
635 
636 static gboolean
str_equal0(gconstpointer a,gconstpointer b)637 str_equal0 (gconstpointer a,
638             gconstpointer b)
639 {
640   return g_strcmp0 (a, b) == 0;
641 }
642 
643 static GFileMonitorSource *
g_file_monitor_source_new(gpointer instance,const gchar * filename,gboolean is_directory,GFileMonitorFlags flags)644 g_file_monitor_source_new (gpointer           instance,
645                            const gchar       *filename,
646                            gboolean           is_directory,
647                            GFileMonitorFlags  flags)
648 {
649   static GSourceFuncs source_funcs = {
650     NULL, NULL,
651     g_file_monitor_source_dispatch,
652     g_file_monitor_source_finalize,
653     NULL, NULL
654   };
655   GFileMonitorSource *fms;
656   GSource *source;
657 
658   source = g_source_new (&source_funcs, sizeof (GFileMonitorSource));
659   fms = (GFileMonitorSource *) source;
660 
661   g_source_set_static_name (source, "GFileMonitorSource");
662 
663   g_mutex_init (&fms->lock);
664   g_weak_ref_init (&fms->instance_ref, instance);
665   fms->pending_changes = g_sequence_new (pending_change_free);
666   fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0);
667   fms->rate_limit = DEFAULT_RATE_LIMIT;
668   fms->flags = flags;
669 
670   if (is_directory)
671     {
672       fms->dirname = g_strdup (filename);
673       fms->basename = NULL;
674       fms->filename = NULL;
675     }
676   else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS)
677     {
678       fms->dirname = NULL;
679       fms->basename = NULL;
680       fms->filename = g_strdup (filename);
681     }
682   else
683     {
684       fms->dirname = g_path_get_dirname (filename);
685       fms->basename = g_path_get_basename (filename);
686       fms->filename = NULL;
687     }
688 
689   return fms;
690 }
691 
692 G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR)
693 
694 enum {
695   PROP_0,
696   PROP_RATE_LIMIT,
697 };
698 
699 static void
g_local_file_monitor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)700 g_local_file_monitor_get_property (GObject *object, guint prop_id,
701                                    GValue *value, GParamSpec *pspec)
702 {
703   GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
704   gint64 rate_limit;
705 
706   g_assert (prop_id == PROP_RATE_LIMIT);
707 
708   rate_limit = g_file_monitor_source_get_rate_limit (monitor->source);
709   rate_limit /= G_TIME_SPAN_MILLISECOND;
710 
711   g_value_set_int (value, rate_limit);
712 }
713 
714 static void
g_local_file_monitor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)715 g_local_file_monitor_set_property (GObject *object, guint prop_id,
716                                    const GValue *value, GParamSpec *pspec)
717 {
718   GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
719   gint64 rate_limit;
720 
721   g_assert (prop_id == PROP_RATE_LIMIT);
722 
723   rate_limit = g_value_get_int (value);
724   rate_limit *= G_TIME_SPAN_MILLISECOND;
725 
726   if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit))
727     g_object_notify (object, "rate-limit");
728 }
729 
730 #ifndef G_OS_WIN32
731 static void
g_local_file_monitor_mounts_changed(GUnixMountMonitor * mount_monitor,gpointer user_data)732 g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor,
733                                      gpointer           user_data)
734 {
735   GLocalFileMonitor *local_monitor = user_data;
736   GUnixMountEntry *mount;
737   gboolean is_mounted;
738   GFile *file;
739 
740   /* Emulate unmount detection */
741   mount = g_unix_mount_at (local_monitor->source->dirname, NULL);
742 
743   is_mounted = mount != NULL;
744 
745   if (mount)
746     g_unix_mount_free (mount);
747 
748   if (local_monitor->was_mounted != is_mounted)
749     {
750       if (local_monitor->was_mounted && !is_mounted)
751         {
752           file = g_file_new_for_path (local_monitor->source->dirname);
753           g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED);
754           g_object_unref (file);
755         }
756       local_monitor->was_mounted = is_mounted;
757     }
758 }
759 #endif
760 
761 static void
g_local_file_monitor_start(GLocalFileMonitor * local_monitor,const gchar * filename,gboolean is_directory,GFileMonitorFlags flags,GMainContext * context)762 g_local_file_monitor_start (GLocalFileMonitor *local_monitor,
763                             const gchar       *filename,
764                             gboolean           is_directory,
765                             GFileMonitorFlags  flags,
766                             GMainContext      *context)
767 {
768   GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor);
769   GFileMonitorSource *source;
770 
771   g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor));
772 
773   g_assert (!local_monitor->source);
774 
775   source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags);
776   local_monitor->source = source; /* owns the ref */
777 
778   if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS))
779     {
780 #ifdef G_OS_WIN32
781       /*claim everything was mounted */
782       local_monitor->was_mounted = TRUE;
783 #else
784       GUnixMountEntry *mount;
785 
786       /* Emulate unmount detection */
787 
788       mount = g_unix_mount_at (local_monitor->source->dirname, NULL);
789 
790       local_monitor->was_mounted = mount != NULL;
791 
792       if (mount)
793         g_unix_mount_free (mount);
794 
795       local_monitor->mount_monitor = g_unix_mount_monitor_get ();
796       g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed",
797                                G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor, 0);
798 #endif
799     }
800 
801   g_source_attach ((GSource *) source, context);
802 
803   G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor,
804                                                          source->dirname, source->basename, source->filename,
805                                                          source);
806 }
807 
808 static void
g_local_file_monitor_dispose(GObject * object)809 g_local_file_monitor_dispose (GObject *object)
810 {
811   GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
812 
813   g_file_monitor_source_dispose (local_monitor->source);
814 
815   G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object);
816 }
817 
818 static void
g_local_file_monitor_finalize(GObject * object)819 g_local_file_monitor_finalize (GObject *object)
820 {
821   GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
822 
823   g_source_unref ((GSource *) local_monitor->source);
824 
825   G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object);
826 }
827 
828 static void
g_local_file_monitor_init(GLocalFileMonitor * local_monitor)829 g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
830 {
831 }
832 
g_local_file_monitor_class_init(GLocalFileMonitorClass * class)833 static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class)
834 {
835   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
836 
837   gobject_class->get_property = g_local_file_monitor_get_property;
838   gobject_class->set_property = g_local_file_monitor_set_property;
839   gobject_class->dispose = g_local_file_monitor_dispose;
840   gobject_class->finalize = g_local_file_monitor_finalize;
841 
842   g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit");
843 }
844 
845 static GLocalFileMonitor *
g_local_file_monitor_new(gboolean is_remote_fs,gboolean is_directory,GError ** error)846 g_local_file_monitor_new (gboolean   is_remote_fs,
847                           gboolean   is_directory,
848                           GError   **error)
849 {
850   GType type = G_TYPE_INVALID;
851 
852   if (is_remote_fs)
853     type = _g_io_module_get_default_type (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
854                                           "GIO_USE_FILE_MONITOR",
855                                           G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
856 
857   /* Fallback rather to poll file monitor for remote files, see gfile.c. */
858   if (type == G_TYPE_INVALID && (!is_remote_fs || is_directory))
859     type = _g_io_module_get_default_type (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
860                                           "GIO_USE_FILE_MONITOR",
861                                           G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
862 
863   if (type == G_TYPE_INVALID)
864     {
865       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
866                            _("Unable to find default local file monitor type"));
867       return NULL;
868     }
869 
870   return g_object_new (type, NULL);
871 }
872 
873 GFileMonitor *
g_local_file_monitor_new_for_path(const gchar * pathname,gboolean is_directory,GFileMonitorFlags flags,GError ** error)874 g_local_file_monitor_new_for_path (const gchar        *pathname,
875                                    gboolean            is_directory,
876                                    GFileMonitorFlags   flags,
877                                    GError            **error)
878 {
879   GLocalFileMonitor *monitor;
880   gboolean is_remote_fs;
881 
882   is_remote_fs = g_local_file_is_nfs_home (pathname);
883 
884   monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
885 
886   if (monitor)
887     g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ());
888 
889   return G_FILE_MONITOR (monitor);
890 }
891 
892 GFileMonitor *
g_local_file_monitor_new_in_worker(const gchar * pathname,gboolean is_directory,GFileMonitorFlags flags,GFileMonitorCallback callback,gpointer user_data,GClosureNotify destroy_user_data,GError ** error)893 g_local_file_monitor_new_in_worker (const gchar           *pathname,
894                                     gboolean               is_directory,
895                                     GFileMonitorFlags      flags,
896                                     GFileMonitorCallback   callback,
897                                     gpointer               user_data,
898                                     GClosureNotify         destroy_user_data,
899                                     GError               **error)
900 {
901   GLocalFileMonitor *monitor;
902   gboolean is_remote_fs;
903 
904   is_remote_fs = g_local_file_is_nfs_home (pathname);
905 
906   monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
907 
908   if (monitor)
909     {
910       if (callback)
911         g_signal_connect_data (monitor, "changed", G_CALLBACK (callback),
912                                user_data, destroy_user_data, 0  /* flags */);
913 
914       g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ());
915     }
916 
917   return G_FILE_MONITOR (monitor);
918 }
919