From 71a5f1b6314dfe26037cc586025e1cd6e2e70ca6 Mon Sep 17 00:00:00 2001 From: Ondrej Holy Date: Mon, 22 Jan 2024 15:29:37 +0100 Subject: [PATCH 1/3] gunixmounts: Use libmnt_monitor API for monitoring The `GUnixMountMonitor` object implements monitoring on its own currently. Only the `/proc/mounts` file changes are monitored. It is not aware of the `/run/mount/utab` file changes. This file contains the userspace mount options (e.g. `x-gvfs-notrash`, `x-gvfs-hide`) among others. There is a problem when `/sbin/mount.` (e.g. `mount.nfs`) helper programs are used. In that case, the `/run/mount/utab` file is updated later than the `/proc/mounts` file and thus the `GUnixMountMonitor` clients (e.g. `gvfs-udisks2-volume-monitor`, `gvfsd-trash`) don't see the userspace options until the next `mount-changed` signal. Let's use the `libmnt_monitor` API for monitoring instead and emit the `mount-changed` signal also when the `/run/mount/utab` file is changed. Related: https://issues.redhat.com/browse/RHEL-14607 Related: https://github.com/util-linux/util-linux/pull/2607 --- gio/gunixmounts.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 4695d5618..5d455ea0b 100644 --- a/gio/gunixmounts.c +++ b/gio/gunixmounts.c @@ -198,6 +198,11 @@ static GSource *proc_mounts_watch_source; #define endmntent(f) fclose(f) #endif +#ifdef HAVE_LIBMOUNT +/* Protected by proc_mounts_source lock */ +static struct libmnt_monitor *proc_mounts_monitor = NULL; +#endif + static gboolean is_in (const char *value, const char *set[]) { @@ -1788,7 +1793,36 @@ proc_mounts_changed (GIOChannel *channel, GIOCondition cond, gpointer user_data) { + gboolean has_changed = FALSE; + +#ifdef HAVE_LIBMOUNT + if (cond & G_IO_IN) + { + G_LOCK (proc_mounts_source); + if (proc_mounts_monitor != NULL) + { + int ret; + + /* The mnt_monitor_next_change function needs to be used to avoid false-positives. */ + ret = mnt_monitor_next_change (proc_mounts_monitor, NULL, NULL); + if (ret == 0) + { + has_changed = TRUE; + ret = mnt_monitor_event_cleanup (proc_mounts_monitor); + } + + if (ret < 0) + g_debug ("mnt_monitor_next_change failed: %s", g_strerror (-ret)); + } + G_UNLOCK (proc_mounts_source); + } + +#else if (cond & G_IO_ERR) + has_changed = TRUE; +#endif + + if (has_changed) { G_LOCK (proc_mounts_source); mount_poller_time = (guint64) g_get_monotonic_time (); @@ -1853,6 +1887,10 @@ mount_monitor_stop (void) g_source_destroy (proc_mounts_watch_source); proc_mounts_watch_source = NULL; } + +#ifdef HAVE_LIBMOUNT + g_clear_pointer (&proc_mounts_monitor, mnt_unref_monitor); +#endif G_UNLOCK (proc_mounts_source); if (mtab_monitor) @@ -1894,9 +1932,37 @@ mount_monitor_start (void) */ if (g_str_has_prefix (mtab_path, "/proc/")) { - GIOChannel *proc_mounts_channel; + GIOChannel *proc_mounts_channel = NULL; GError *error = NULL; +#ifdef HAVE_LIBMOUNT + int ret; + + G_LOCK (proc_mounts_source); + + proc_mounts_monitor = mnt_new_monitor (); + ret = mnt_monitor_enable_kernel (proc_mounts_monitor, TRUE); + if (ret < 0) + g_warning ("mnt_monitor_enable_kernel failed: %s", g_strerror (-ret)); + + ret = mnt_monitor_enable_userspace (proc_mounts_monitor, TRUE, NULL); + if (ret < 0) + g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret)); + + ret = mnt_monitor_get_fd (proc_mounts_monitor); + if (ret >= 0) + { + proc_mounts_channel = g_io_channel_unix_new (ret); + } + else + { + g_set_error_literal (&error, G_IO_ERROR, g_io_error_from_errno (-ret), + g_strerror (-ret)); + } + + G_UNLOCK (proc_mounts_source); +#else proc_mounts_channel = g_io_channel_new_file (mtab_path, "r", &error); +#endif if (proc_mounts_channel == NULL) { g_warning ("Error creating IO channel for %s: %s (%s, %d)", mtab_path, @@ -1907,7 +1973,11 @@ mount_monitor_start (void) { G_LOCK (proc_mounts_source); +#ifdef HAVE_LIBMOUNT + proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_IN); +#else proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR); +#endif mount_poller_time = (guint64) g_get_monotonic_time (); g_source_set_callback (proc_mounts_watch_source, (GSourceFunc) proc_mounts_changed, -- 2.43.0 From 5ee14937831eec7384bbe4a63a61aa8edfbc801d Mon Sep 17 00:00:00 2001 From: Ondrej Holy Date: Wed, 31 Jan 2024 13:35:39 +0100 Subject: [PATCH 2/3] gunixmounts: Use mnt_monitor_veil_kernel option The previous commit enabled the `/run/mount/utab` monitoring. The problem is that the `mount-changed` signal can be emitted twice for one mount. One for the `/proc/mounts` file change and another one for the `/run/media/utab` file change. This is still not ideal because e.g. the `GMount` objects for mounts with the `x-gvfs-hide` option are added and immediately removed. Let's enable the `mnt_monitor_veil_kernel` option to avoid this. Related: https://github.com/util-linux/util-linux/pull/2725 --- gio/gunixmounts.c | 6 ++++++ meson.build | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 5d455ea0b..b8f4608d6 100644 --- a/gio/gunixmounts.c +++ b/gio/gunixmounts.c @@ -1948,6 +1948,12 @@ mount_monitor_start (void) if (ret < 0) g_warning ("mnt_monitor_enable_userspace failed: %s", g_strerror (-ret)); +#ifdef HAVE_MNT_MONITOR_VEIL_KERNEL + ret = mnt_monitor_veil_kernel (proc_mounts_monitor, TRUE); + if (ret < 0) + g_warning ("mnt_monitor_veil_kernel failed: %s", g_strerror (-ret)); +#endif + ret = mnt_monitor_get_fd (proc_mounts_monitor); if (ret >= 0) { diff --git a/meson.build b/meson.build index eaf8d3900..7e98fd426 100644 --- a/meson.build +++ b/meson.build @@ -1592,8 +1592,13 @@ xgettext = find_program('xgettext', required : false) # libmount is only used by gio, but we need to fetch the libs to generate the # pkg-config file below libmount_dep = [] -if host_system == 'linux' and get_option('libmount') - libmount_dep = [dependency('mount', version : '>=2.23', required : true)] +if host_system == 'linux' + libmount_dep = dependency('mount', version : '>=2.23', required : get_option('libmount')) + glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) + + if libmount_dep.found() and cc.has_function('mnt_monitor_veil_kernel', dependencies: libmount_dep) + glib_conf.set('HAVE_MNT_MONITOR_VEIL_KERNEL', 1) + endif endif # gnutls is used optionally by ghmac -- 2.43.0 From 84b74be1efd4877bc476f1209c701b1a744a299b Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Thu, 1 Feb 2024 19:17:43 -0600 Subject: [PATCH 3/3] Adjust meson.build for older syntax This isn't technically correct because libmount could be found but disabled, but that's OK. In RHEL, it's enabled. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 7e98fd426..aeaa99143 100644 --- a/meson.build +++ b/meson.build @@ -1686,7 +1686,7 @@ endif if use_system_pcre glib_conf.set('PCRE_LIBS', '-lpcre') endif -if libmount_dep.length() != 0 +if libmount_dep.found() glib_conf.set('LIBMOUNT_LIBS', '-lmount') glib_conf.set('HAVE_LIBMOUNT', 1) endif -- 2.43.0