Coverage Report

Created: 2025-07-01 07:09

/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
 * 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
0
#define DEFAULT_RATE_LIMIT                           800 * G_TIME_SPAN_MILLISECOND
34
0
#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
86
pending_change_get_ready_time (const PendingChange *change,
87
                               GFileMonitorSource  *fms)
88
0
{
89
0
  if (change->dirty)
90
0
    return change->last_emission + fms->rate_limit;
91
0
  else
92
0
    return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY;
93
0
}
94
95
static int
96
pending_change_compare_ready_time (gconstpointer a_p,
97
                                   gconstpointer b_p,
98
                                   gpointer      user_data)
99
0
{
100
0
  GFileMonitorSource *fms = user_data;
101
0
  const PendingChange *a = a_p;
102
0
  const PendingChange *b = b_p;
103
0
  gint64 ready_time_a;
104
0
  gint64 ready_time_b;
105
106
0
  ready_time_a = pending_change_get_ready_time (a, fms);
107
0
  ready_time_b = pending_change_get_ready_time (b, fms);
108
109
0
  if (ready_time_a < ready_time_b)
110
0
    return -1;
111
0
  else
112
0
    return ready_time_a > ready_time_b;
113
0
}
114
115
static void
116
pending_change_free (gpointer data)
117
0
{
118
0
  PendingChange *change = data;
119
120
0
  g_free (change->child);
121
122
0
  g_slice_free (PendingChange, change);
123
0
}
124
125
static void
126
queued_event_free (QueuedEvent *event)
127
0
{
128
0
  g_object_unref (event->child);
129
0
  if (event->other)
130
0
    g_object_unref (event->other);
131
132
0
  g_slice_free (QueuedEvent, event);
133
0
}
134
135
static gint64
136
g_file_monitor_source_get_ready_time (GFileMonitorSource *fms)
137
0
{
138
0
  GSequenceIter *iter;
139
140
0
  if (fms->event_queue.length)
141
0
    return 0;
142
143
0
  iter = g_sequence_get_begin_iter (fms->pending_changes);
144
0
  if (g_sequence_iter_is_end (iter))
145
0
    return -1;
146
147
0
  return pending_change_get_ready_time (g_sequence_get (iter), fms);
148
0
}
149
150
static void
151
g_file_monitor_source_update_ready_time (GFileMonitorSource *fms)
152
0
{
153
0
  g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms));
154
0
}
155
156
static GSequenceIter *
157
g_file_monitor_source_find_pending_change (GFileMonitorSource *fms,
158
                                           const gchar        *child)
159
0
{
160
0
  return g_hash_table_lookup (fms->pending_changes_table, child);
161
0
}
162
163
static void
164
g_file_monitor_source_add_pending_change (GFileMonitorSource *fms,
165
                                          const gchar        *child,
166
                                          gint64              now)
167
0
{
168
0
  PendingChange *change;
169
0
  GSequenceIter *iter;
170
171
0
  change = g_slice_new (PendingChange);
172
0
  change->child = g_strdup (child);
173
0
  change->last_emission = now;
174
0
  change->dirty = FALSE;
175
176
0
  iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms);
177
0
  g_hash_table_insert (fms->pending_changes_table, change->child, iter);
178
0
}
179
180
static gboolean
181
g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms,
182
                                                GSequenceIter      *iter)
183
0
{
184
0
  PendingChange *change;
185
186
0
  change = g_sequence_get (iter);
187
188
  /* if it was already dirty then this change is 'uninteresting' */
189
0
  if (change->dirty)
190
0
    return FALSE;
191
192
0
  change->dirty = TRUE;
193
194
0
  g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
195
196
0
  return TRUE;
197
0
}
198
199
static gboolean
200
g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms,
201
                                                GSequenceIter      *iter)
202
0
{
203
0
  PendingChange *change;
204
205
0
  change = g_sequence_get (iter);
206
207
0
  return change->dirty;
208
0
}
209
210
static void
211
g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms,
212
                                             GSequenceIter      *iter,
213
                                             const gchar        *child)
214
0
{
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
0
  g_hash_table_remove (fms->pending_changes_table, child);
219
0
  g_sequence_remove (iter);
220
0
}
221
222
static void
223
g_file_monitor_source_queue_event (GFileMonitorSource *fms,
224
                                   GFileMonitorEvent   event_type,
225
                                   const gchar        *child,
226
                                   GFile              *other)
227
0
{
228
0
  QueuedEvent *event;
229
230
0
  event = g_slice_new (QueuedEvent);
231
0
  event->event_type = event_type;
232
0
  if (child != NULL && fms->dirname != NULL)
233
0
    event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child);
234
0
  else if (child != NULL)
235
0
    {
236
0
      gchar *dirname = g_path_get_dirname (fms->filename);
237
0
      event->child = g_local_file_new_from_dirname_and_basename (dirname, child);
238
0
      g_free (dirname);
239
0
    }
240
0
  else if (fms->dirname)
241
0
    event->child = _g_local_file_new (fms->dirname);
242
0
  else if (fms->filename)
243
0
    event->child = _g_local_file_new (fms->filename);
244
0
  event->other = other;
245
0
  if (other)
246
0
    g_object_ref (other);
247
248
0
  g_queue_push_tail (&fms->event_queue, event);
249
0
}
250
251
static gboolean
252
g_file_monitor_source_file_changed (GFileMonitorSource *fms,
253
                                    const gchar        *child,
254
                                    gint64              now)
255
0
{
256
0
  GSequenceIter *pending;
257
0
  gboolean interesting;
258
259
0
  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
0
  if (!pending)
265
0
    {
266
0
      g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
267
0
      g_file_monitor_source_add_pending_change (fms, child, now);
268
0
      interesting = TRUE;
269
0
    }
270
0
  else
271
0
    interesting = g_file_monitor_source_set_pending_change_dirty (fms, pending);
272
273
0
  g_file_monitor_source_update_ready_time (fms);
274
275
0
  return interesting;
276
0
}
277
278
static void
279
g_file_monitor_source_file_changes_done (GFileMonitorSource *fms,
280
                                         const gchar        *child)
281
0
{
282
0
  GSequenceIter *pending;
283
284
0
  pending = g_file_monitor_source_find_pending_change (fms, child);
285
0
  if (pending)
286
0
    {
287
      /* If it is dirty, make sure we push out the last CHANGED event */
288
0
      if (g_file_monitor_source_get_pending_change_dirty (fms, pending))
289
0
        g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
290
291
0
      g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
292
0
      g_file_monitor_source_remove_pending_change (fms, pending, child);
293
0
    }
294
0
}
295
296
static void
297
g_file_monitor_source_file_created (GFileMonitorSource *fms,
298
                                    const gchar        *child,
299
                                    gint64              event_time)
300
0
{
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
0
  g_file_monitor_source_file_changes_done (fms, child);
305
306
  /* Emit CREATE and add a pending changes record */
307
0
  g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
308
0
  g_file_monitor_source_add_pending_change (fms, child, event_time);
309
0
}
310
311
static void
312
g_file_monitor_source_send_event (GFileMonitorSource *fms,
313
                                  GFileMonitorEvent   event_type,
314
                                  const gchar        *child,
315
                                  GFile              *other)
316
0
{
317
  /* always flush any pending changes before we queue a new event */
318
0
  g_file_monitor_source_file_changes_done (fms, child);
319
0
  g_file_monitor_source_queue_event (fms, event_type, child, other);
320
0
}
321
322
static void
323
g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms,
324
                                              const gchar        *child)
325
0
{
326
0
  g_file_monitor_source_file_changes_done (fms, child);
327
0
  g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
328
0
  g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
329
0
}
330
331
#ifndef G_DISABLE_ASSERT
332
static gboolean
333
is_basename (const gchar *name)
334
0
{
335
0
  if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0'))
336
0
    return FALSE;
337
338
0
  return !strchr (name, '/');
339
0
}
340
#endif  /* !G_DISABLE_ASSERT */
341
342
gboolean
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
0
{
350
0
  gboolean interesting = TRUE;
351
0
  GFileMonitor *instance = NULL;
352
353
0
  g_assert (!child || is_basename (child));
354
0
  g_assert (!rename_to || is_basename (rename_to));
355
356
0
  if (fms->basename && (!child || !g_str_equal (child, fms->basename))
357
0
                    && (!rename_to || !g_str_equal (rename_to, fms->basename)))
358
0
    return TRUE;
359
360
0
  g_mutex_lock (&fms->lock);
361
362
  /* monitor is already gone -- don't bother */
363
0
  instance = g_weak_ref_get (&fms->instance_ref);
364
0
  if (instance == NULL)
365
0
    {
366
0
      g_mutex_unlock (&fms->lock);
367
0
      return TRUE;
368
0
    }
369
370
0
  switch (event_type)
371
0
    {
372
0
    case G_FILE_MONITOR_EVENT_CREATED:
373
0
      g_assert (!other && !rename_to);
374
0
      g_file_monitor_source_file_created (fms, child, event_time);
375
0
      break;
376
377
0
    case G_FILE_MONITOR_EVENT_CHANGED:
378
0
      g_assert (!other && !rename_to);
379
0
      interesting = g_file_monitor_source_file_changed (fms, child, event_time);
380
0
      break;
381
382
0
    case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
383
0
      g_assert (!other && !rename_to);
384
0
      g_file_monitor_source_file_changes_done (fms, child);
385
0
      break;
386
387
0
    case G_FILE_MONITOR_EVENT_MOVED_IN:
388
0
      g_assert (!rename_to);
389
0
      if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
390
0
        g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other);
391
0
      else
392
0
        g_file_monitor_source_send_synthetic_created (fms, child);
393
0
      break;
394
395
0
    case G_FILE_MONITOR_EVENT_MOVED_OUT:
396
0
      g_assert (!rename_to);
397
0
      if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
398
0
        g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other);
399
0
      else if (other && (fms->flags & G_FILE_MONITOR_SEND_MOVED))
400
0
        g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other);
401
0
      else
402
0
        g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
403
0
      break;
404
405
0
    case G_FILE_MONITOR_EVENT_RENAMED:
406
0
      g_assert (!other && rename_to);
407
0
      if (fms->flags & (G_FILE_MONITOR_WATCH_MOVES | G_FILE_MONITOR_SEND_MOVED))
408
0
        {
409
0
          GFile *other;
410
0
          const gchar *dirname;
411
0
          gchar *allocated_dirname = NULL;
412
0
          GFileMonitorEvent event;
413
414
0
          event = (fms->flags & G_FILE_MONITOR_WATCH_MOVES) ? G_FILE_MONITOR_EVENT_RENAMED : G_FILE_MONITOR_EVENT_MOVED;
415
416
0
          if (fms->dirname != NULL)
417
0
            dirname = fms->dirname;
418
0
          else
419
0
            {
420
0
              allocated_dirname = g_path_get_dirname (fms->filename);
421
0
              dirname = allocated_dirname;
422
0
            }
423
424
0
          other = g_local_file_new_from_dirname_and_basename (dirname, rename_to);
425
0
          g_file_monitor_source_file_changes_done (fms, rename_to);
426
0
          g_file_monitor_source_send_event (fms, event, child, other);
427
428
0
          g_object_unref (other);
429
0
          g_free (allocated_dirname);
430
0
        }
431
0
      else
432
0
        {
433
0
          g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
434
0
          g_file_monitor_source_send_synthetic_created (fms, rename_to);
435
0
        }
436
0
      break;
437
438
0
    case G_FILE_MONITOR_EVENT_DELETED:
439
0
    case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
440
0
    case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
441
0
    case G_FILE_MONITOR_EVENT_UNMOUNTED:
442
0
      g_assert (!other && !rename_to);
443
0
      g_file_monitor_source_send_event (fms, event_type, child, NULL);
444
0
      break;
445
446
0
    case G_FILE_MONITOR_EVENT_MOVED:
447
      /* was never available in this API */
448
0
    default:
449
0
      g_assert_not_reached ();
450
0
    }
451
452
0
  g_file_monitor_source_update_ready_time (fms);
453
454
0
  g_mutex_unlock (&fms->lock);
455
0
  g_clear_object (&instance);
456
457
0
  return interesting;
458
0
}
459
460
static gint64
461
g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms)
462
0
{
463
0
  gint64 rate_limit;
464
465
0
  g_mutex_lock (&fms->lock);
466
0
  rate_limit = fms->rate_limit;
467
0
  g_mutex_unlock (&fms->lock);
468
469
0
  return rate_limit;
470
0
}
471
472
static gboolean
473
g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms,
474
                                      gint64              rate_limit)
475
0
{
476
0
  gboolean changed;
477
478
0
  g_mutex_lock (&fms->lock);
479
480
0
  if (rate_limit != fms->rate_limit)
481
0
    {
482
0
      fms->rate_limit = rate_limit;
483
484
0
      g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms);
485
0
      g_file_monitor_source_update_ready_time (fms);
486
487
0
      changed = TRUE;
488
0
    }
489
0
  else
490
0
    changed = FALSE;
491
492
0
  g_mutex_unlock (&fms->lock);
493
494
0
  return changed;
495
0
}
496
497
static gboolean
498
g_file_monitor_source_dispatch (GSource     *source,
499
                                GSourceFunc  callback,
500
                                gpointer     user_data)
501
0
{
502
0
  GFileMonitorSource *fms = (GFileMonitorSource *) source;
503
0
  QueuedEvent *event;
504
0
  GQueue event_queue;
505
0
  gint64 now;
506
0
  GFileMonitor *instance = NULL;
507
508
  /* make sure the monitor still exists */
509
0
  instance = g_weak_ref_get (&fms->instance_ref);
510
0
  if (instance == NULL)
511
0
    return FALSE;
512
513
0
  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
0
  g_mutex_lock (&fms->lock);
524
525
  /* Create events for any pending changes that are due to fire */
526
0
  while (!g_sequence_is_empty (fms->pending_changes))
527
0
    {
528
0
      GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes);
529
0
      PendingChange *pending = g_sequence_get (iter);
530
531
      /* We've gotten to a pending change that's not ready.  Stop. */
532
0
      if (pending_change_get_ready_time (pending, fms) > now)
533
0
        break;
534
535
0
      if (pending->dirty)
536
0
        {
537
          /* It's time to send another CHANGED and update the record */
538
0
          g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL);
539
0
          pending->last_emission = now;
540
0
          pending->dirty = FALSE;
541
542
0
          g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
543
0
        }
544
0
      else
545
0
        {
546
          /* It's time to send CHANGES_DONE and remove the pending record */
547
0
          g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL);
548
0
          g_file_monitor_source_remove_pending_change (fms, iter, pending->child);
549
0
        }
550
0
    }
551
552
  /* Steal the queue */
553
0
  memcpy (&event_queue, &fms->event_queue, sizeof event_queue);
554
0
  memset (&fms->event_queue, 0, sizeof fms->event_queue);
555
556
0
  g_file_monitor_source_update_ready_time (fms);
557
558
0
  g_mutex_unlock (&fms->lock);
559
0
  g_clear_object (&instance);
560
561
  /* We now have our list of events to deliver */
562
0
  while ((event = g_queue_pop_head (&event_queue)))
563
0
    {
564
      /* an event handler could destroy 'instance', so check each time */
565
0
      instance = g_weak_ref_get (&fms->instance_ref);
566
0
      if (instance != NULL)
567
0
        g_file_monitor_emit_event (instance, event->child, event->other, event->event_type);
568
569
0
      g_clear_object (&instance);
570
0
      queued_event_free (event);
571
0
    }
572
573
0
  return TRUE;
574
0
}
575
576
static void
577
g_file_monitor_source_dispose (GFileMonitorSource *fms)
578
0
{
579
0
  GHashTableIter iter;
580
0
  gpointer seqiter;
581
0
  QueuedEvent *event;
582
583
0
  g_mutex_lock (&fms->lock);
584
585
0
  g_hash_table_iter_init (&iter, fms->pending_changes_table);
586
0
  while (g_hash_table_iter_next (&iter, NULL, &seqiter))
587
0
    {
588
0
      g_hash_table_iter_remove (&iter);
589
0
      g_sequence_remove (seqiter);
590
0
    }
591
592
0
  while ((event = g_queue_pop_head (&fms->event_queue)))
593
0
    queued_event_free (event);
594
595
0
  g_assert (g_sequence_is_empty (fms->pending_changes));
596
0
  g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
597
0
  g_assert (fms->event_queue.length == 0);
598
0
  g_weak_ref_set (&fms->instance_ref, NULL);
599
600
0
  g_file_monitor_source_update_ready_time (fms);
601
602
0
  g_mutex_unlock (&fms->lock);
603
604
0
  g_source_destroy ((GSource *) fms);
605
0
}
606
607
static void
608
g_file_monitor_source_finalize (GSource *source)
609
0
{
610
0
  GFileMonitorSource *fms = (GFileMonitorSource *) source;
611
612
  /* should already have been cleared in dispose of the monitor */
613
0
  g_assert (g_weak_ref_get (&fms->instance_ref) == NULL);
614
0
  g_weak_ref_clear (&fms->instance_ref);
615
616
0
  g_assert (g_sequence_is_empty (fms->pending_changes));
617
0
  g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
618
0
  g_assert (fms->event_queue.length == 0);
619
620
0
  g_hash_table_unref (fms->pending_changes_table);
621
0
  g_sequence_free (fms->pending_changes);
622
623
0
  g_free (fms->dirname);
624
0
  g_free (fms->basename);
625
0
  g_free (fms->filename);
626
627
0
  g_mutex_clear (&fms->lock);
628
0
}
629
630
static guint
631
str_hash0 (gconstpointer str)
632
0
{
633
0
  return str ? g_str_hash (str) : 0;
634
0
}
635
636
static gboolean
637
str_equal0 (gconstpointer a,
638
            gconstpointer b)
639
0
{
640
0
  return g_strcmp0 (a, b) == 0;
641
0
}
642
643
static GFileMonitorSource *
644
g_file_monitor_source_new (gpointer           instance,
645
                           const gchar       *filename,
646
                           gboolean           is_directory,
647
                           GFileMonitorFlags  flags)
648
0
{
649
0
  static GSourceFuncs source_funcs = {
650
0
    NULL, NULL,
651
0
    g_file_monitor_source_dispatch,
652
0
    g_file_monitor_source_finalize,
653
0
    NULL, NULL
654
0
  };
655
0
  GFileMonitorSource *fms;
656
0
  GSource *source;
657
658
0
  source = g_source_new (&source_funcs, sizeof (GFileMonitorSource));
659
0
  fms = (GFileMonitorSource *) source;
660
661
0
  g_source_set_name (source, "GFileMonitorSource");
662
663
0
  g_mutex_init (&fms->lock);
664
0
  g_weak_ref_init (&fms->instance_ref, instance);
665
0
  fms->pending_changes = g_sequence_new (pending_change_free);
666
0
  fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0);
667
0
  fms->rate_limit = DEFAULT_RATE_LIMIT;
668
0
  fms->flags = flags;
669
670
0
  if (is_directory)
671
0
    {
672
0
      fms->dirname = g_strdup (filename);
673
0
      fms->basename = NULL;
674
0
      fms->filename = NULL;
675
0
    }
676
0
  else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS)
677
0
    {
678
0
      fms->dirname = NULL;
679
0
      fms->basename = NULL;
680
0
      fms->filename = g_strdup (filename);
681
0
    }
682
0
  else
683
0
    {
684
0
      fms->dirname = g_path_get_dirname (filename);
685
0
      fms->basename = g_path_get_basename (filename);
686
0
      fms->filename = NULL;
687
0
    }
688
689
0
  return fms;
690
0
}
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
700
g_local_file_monitor_get_property (GObject *object, guint prop_id,
701
                                   GValue *value, GParamSpec *pspec)
702
0
{
703
0
  GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
704
0
  gint64 rate_limit;
705
706
0
  g_assert (prop_id == PROP_RATE_LIMIT);
707
708
0
  rate_limit = g_file_monitor_source_get_rate_limit (monitor->source);
709
0
  rate_limit /= G_TIME_SPAN_MILLISECOND;
710
711
0
  g_value_set_int (value, rate_limit);
712
0
}
713
714
static void
715
g_local_file_monitor_set_property (GObject *object, guint prop_id,
716
                                   const 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_value_get_int (value);
724
0
  rate_limit *= G_TIME_SPAN_MILLISECOND;
725
726
0
  if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit))
727
0
    g_object_notify (object, "rate-limit");
728
0
}
729
730
#ifndef G_OS_WIN32
731
static void
732
g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor,
733
                                     gpointer           user_data)
734
0
{
735
0
  GLocalFileMonitor *local_monitor = user_data;
736
0
  GUnixMountEntry *mount;
737
0
  gboolean is_mounted;
738
0
  GFile *file;
739
740
  /* Emulate unmount detection */
741
0
  mount = g_unix_mount_at (local_monitor->source->dirname, NULL);
742
743
0
  is_mounted = mount != NULL;
744
745
0
  if (mount)
746
0
    g_unix_mount_free (mount);
747
748
0
  if (local_monitor->was_mounted != is_mounted)
749
0
    {
750
0
      if (local_monitor->was_mounted && !is_mounted)
751
0
        {
752
0
          file = g_file_new_for_path (local_monitor->source->dirname);
753
0
          g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED);
754
0
          g_object_unref (file);
755
0
        }
756
0
      local_monitor->was_mounted = is_mounted;
757
0
    }
758
0
}
759
#endif
760
761
static void
762
g_local_file_monitor_start (GLocalFileMonitor *local_monitor,
763
                            const gchar       *filename,
764
                            gboolean           is_directory,
765
                            GFileMonitorFlags  flags,
766
                            GMainContext      *context)
767
0
{
768
0
  GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor);
769
0
  GFileMonitorSource *source;
770
771
0
  g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor));
772
773
0
  g_assert (!local_monitor->source);
774
775
0
  source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags);
776
0
  local_monitor->source = source; /* owns the ref */
777
778
0
  if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS))
779
0
    {
780
#ifdef G_OS_WIN32
781
      /*claim everything was mounted */
782
      local_monitor->was_mounted = TRUE;
783
#else
784
0
      GUnixMountEntry *mount;
785
786
      /* Emulate unmount detection */
787
788
0
      mount = g_unix_mount_at (local_monitor->source->dirname, NULL);
789
790
0
      local_monitor->was_mounted = mount != NULL;
791
792
0
      if (mount)
793
0
        g_unix_mount_free (mount);
794
795
0
      local_monitor->mount_monitor = g_unix_mount_monitor_get ();
796
0
      g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed",
797
0
                               G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor, 0);
798
0
#endif
799
0
    }
800
801
0
  g_source_attach ((GSource *) source, context);
802
803
0
  G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor,
804
0
                                                         source->dirname, source->basename, source->filename,
805
0
                                                         source);
806
0
}
807
808
static void
809
g_local_file_monitor_dispose (GObject *object)
810
0
{
811
0
  GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
812
813
0
  g_file_monitor_source_dispose (local_monitor->source);
814
815
0
  G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object);
816
0
}
817
818
static void
819
g_local_file_monitor_finalize (GObject *object)
820
0
{
821
0
  GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
822
823
0
  g_source_unref ((GSource *) local_monitor->source);
824
825
0
  G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object);
826
0
}
827
828
static void
829
g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
830
0
{
831
0
}
832
833
static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class)
834
0
{
835
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
836
837
0
  gobject_class->get_property = g_local_file_monitor_get_property;
838
0
  gobject_class->set_property = g_local_file_monitor_set_property;
839
0
  gobject_class->dispose = g_local_file_monitor_dispose;
840
0
  gobject_class->finalize = g_local_file_monitor_finalize;
841
842
0
  g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit");
843
0
}
844
845
static GLocalFileMonitor *
846
g_local_file_monitor_new (gboolean   is_remote_fs,
847
                          gboolean   is_directory,
848
                          GError   **error)
849
0
{
850
0
  GType type = G_TYPE_INVALID;
851
852
0
  if (is_remote_fs)
853
0
    type = _g_io_module_get_default_type (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
854
0
                                          "GIO_USE_FILE_MONITOR",
855
0
                                          G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
856
857
  /* Fallback rather to poll file monitor for remote files, see gfile.c. */
858
0
  if (type == G_TYPE_INVALID && (!is_remote_fs || is_directory))
859
0
    type = _g_io_module_get_default_type (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
860
0
                                          "GIO_USE_FILE_MONITOR",
861
0
                                          G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
862
863
0
  if (type == G_TYPE_INVALID)
864
0
    {
865
0
      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
866
0
                           _("Unable to find default local file monitor type"));
867
0
      return NULL;
868
0
    }
869
870
0
  return g_object_new (type, NULL);
871
0
}
872
873
GFileMonitor *
874
g_local_file_monitor_new_for_path (const gchar        *pathname,
875
                                   gboolean            is_directory,
876
                                   GFileMonitorFlags   flags,
877
                                   GError            **error)
878
0
{
879
0
  GLocalFileMonitor *monitor;
880
0
  gboolean is_remote_fs;
881
882
0
  is_remote_fs = g_local_file_is_nfs_home (pathname);
883
884
0
  monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
885
886
0
  if (monitor)
887
0
    g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ());
888
889
0
  return G_FILE_MONITOR (monitor);
890
0
}
891
892
GFileMonitor *
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
0
{
901
0
  GLocalFileMonitor *monitor;
902
0
  gboolean is_remote_fs;
903
904
0
  is_remote_fs = g_local_file_is_nfs_home (pathname);
905
906
0
  monitor = g_local_file_monitor_new (is_remote_fs, is_directory, error);
907
908
0
  if (monitor)
909
0
    {
910
0
      if (callback)
911
0
        g_signal_connect_data (monitor, "changed", G_CALLBACK (callback),
912
0
                               user_data, destroy_user_data, 0  /* flags */);
913
914
0
      g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ());
915
0
    }
916
917
0
  return G_FILE_MONITOR (monitor);
918
0
}