Coverage Report

Created: 2025-06-13 06:55

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