gunixmounts: Make GUnixMountMonitor thread-safe

The Nautilus test suite often crashes with "GLib-FATAL-CRITICAL:
g_source_is_destroyed: assertion 'g_atomic_int_get (&source->ref_count)
> 0' failed" if it is started with "GIO_USE_VOLUME_MONITOR=unix". This
is because GUnixMountMonitor is simultaneously used from multiple
threads over GLocalFile and GVolumeMonitor APIs. Let's add guards for
proc_mounts_watch_source and mount_poller_time variables to prevent
those crashes.

Fixes: https://gitlab.gnome.org/GNOME/glib/issues/2030
This commit is contained in:
Ondrej Holy
2020-02-10 16:00:42 +01:00
parent 4a153abebe
commit 972b977659

View File

@@ -152,7 +152,11 @@ static GList *_g_get_unix_mounts (void);
static GList *_g_get_unix_mount_points (void); static GList *_g_get_unix_mount_points (void);
static gboolean proc_mounts_watch_is_running (void); static gboolean proc_mounts_watch_is_running (void);
G_LOCK_DEFINE_STATIC (proc_mounts_source);
/* Protected by proc_mounts_source lock */
static guint64 mount_poller_time = 0; static guint64 mount_poller_time = 0;
static GSource *proc_mounts_watch_source;
#ifdef HAVE_SYS_MNTTAB_H #ifdef HAVE_SYS_MNTTAB_H
#define MNTOPT_RO "ro" #define MNTOPT_RO "ro"
@@ -1485,18 +1489,21 @@ get_mounts_timestamp (void)
{ {
const char *monitor_file; const char *monitor_file;
struct stat buf; struct stat buf;
guint64 timestamp = 0;
G_LOCK (proc_mounts_source);
monitor_file = get_mtab_monitor_file (); monitor_file = get_mtab_monitor_file ();
/* Don't return mtime for /proc/ files */ /* Don't return mtime for /proc/ files */
if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/")) if (monitor_file && !g_str_has_prefix (monitor_file, "/proc/"))
{ {
if (stat (monitor_file, &buf) == 0) if (stat (monitor_file, &buf) == 0)
return (guint64)buf.st_mtime; timestamp = buf.st_mtime;
} }
else if (proc_mounts_watch_is_running ()) else if (proc_mounts_watch_is_running ())
{ {
/* it's being monitored by poll, so return mount_poller_time */ /* it's being monitored by poll, so return mount_poller_time */
return mount_poller_time; timestamp = mount_poller_time;
} }
else else
{ {
@@ -1505,9 +1512,12 @@ get_mounts_timestamp (void)
* return TRUE so any application caches depending on it (like eg. * return TRUE so any application caches depending on it (like eg.
* the one in GIO) get invalidated and don't hold possibly outdated * the one in GIO) get invalidated and don't hold possibly outdated
* data - see Bug 787731 */ * data - see Bug 787731 */
return (guint64) g_get_monotonic_time (); timestamp = g_get_monotonic_time ();
} }
return 0;
G_UNLOCK (proc_mounts_source);
return timestamp;
} }
static guint64 static guint64
@@ -1704,10 +1714,10 @@ G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT)
static GContextSpecificGroup mount_monitor_group; static GContextSpecificGroup mount_monitor_group;
static GFileMonitor *fstab_monitor; static GFileMonitor *fstab_monitor;
static GFileMonitor *mtab_monitor; static GFileMonitor *mtab_monitor;
static GSource *proc_mounts_watch_source;
static GList *mount_poller_mounts; static GList *mount_poller_mounts;
static guint mtab_file_changed_id; static guint mtab_file_changed_id;
/* Called with proc_mounts_source lock held. */
static gboolean static gboolean
proc_mounts_watch_is_running (void) proc_mounts_watch_is_running (void)
{ {
@@ -1768,7 +1778,10 @@ proc_mounts_changed (GIOChannel *channel,
{ {
if (cond & G_IO_ERR) if (cond & G_IO_ERR)
{ {
G_LOCK (proc_mounts_source);
mount_poller_time = (guint64) g_get_monotonic_time (); mount_poller_time = (guint64) g_get_monotonic_time ();
G_UNLOCK (proc_mounts_source);
g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]); g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
} }
@@ -1802,7 +1815,10 @@ mount_change_poller (gpointer user_data)
if (has_changed) if (has_changed)
{ {
G_LOCK (proc_mounts_source);
mount_poller_time = (guint64) g_get_monotonic_time (); mount_poller_time = (guint64) g_get_monotonic_time ();
G_UNLOCK (proc_mounts_source);
g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]); g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
} }
@@ -1819,11 +1835,13 @@ mount_monitor_stop (void)
g_object_unref (fstab_monitor); g_object_unref (fstab_monitor);
} }
G_LOCK (proc_mounts_source);
if (proc_mounts_watch_source != NULL) if (proc_mounts_watch_source != NULL)
{ {
g_source_destroy (proc_mounts_watch_source); g_source_destroy (proc_mounts_watch_source);
proc_mounts_watch_source = NULL; proc_mounts_watch_source = NULL;
} }
G_UNLOCK (proc_mounts_source);
if (mtab_monitor) if (mtab_monitor)
{ {
@@ -1869,6 +1887,8 @@ mount_monitor_start (void)
} }
else else
{ {
G_LOCK (proc_mounts_source);
proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR); proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
g_source_set_callback (proc_mounts_watch_source, g_source_set_callback (proc_mounts_watch_source,
(GSourceFunc) proc_mounts_changed, (GSourceFunc) proc_mounts_changed,
@@ -1877,6 +1897,8 @@ mount_monitor_start (void)
g_main_context_get_thread_default ()); g_main_context_get_thread_default ());
g_source_unref (proc_mounts_watch_source); g_source_unref (proc_mounts_watch_source);
g_io_channel_unref (proc_mounts_channel); g_io_channel_unref (proc_mounts_channel);
G_UNLOCK (proc_mounts_source);
} }
} }
else else
@@ -1889,6 +1911,8 @@ mount_monitor_start (void)
} }
else else
{ {
G_LOCK (proc_mounts_source);
proc_mounts_watch_source = g_timeout_source_new_seconds (3); proc_mounts_watch_source = g_timeout_source_new_seconds (3);
mount_poller_mounts = _g_get_unix_mounts (); mount_poller_mounts = _g_get_unix_mounts ();
mount_poller_time = (guint64)g_get_monotonic_time (); mount_poller_time = (guint64)g_get_monotonic_time ();
@@ -1898,6 +1922,8 @@ mount_monitor_start (void)
g_source_attach (proc_mounts_watch_source, g_source_attach (proc_mounts_watch_source,
g_main_context_get_thread_default ()); g_main_context_get_thread_default ());
g_source_unref (proc_mounts_watch_source); g_source_unref (proc_mounts_watch_source);
G_UNLOCK (proc_mounts_source);
} }
} }