From a33223d1b71cde9e1f91e01caab08d47468ebc4d Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 22 Jan 2026 10:58:59 +0100 Subject: [PATCH] udisks2: Use hash table for quicker lookups in the monitor Resolves: RHEL-143934 --- gvfs.spec | 11 +- udisks2-monitor-performance-202.patch | 109 ++ udisks2-monitor-performance-230.patch | 39 + udisks2-monitor-performance-273.patch | 1741 +++++++++++++++++++++++++ udisks2-monitor-performance-290.patch | 90 ++ 5 files changed, 1989 insertions(+), 1 deletion(-) create mode 100644 udisks2-monitor-performance-202.patch create mode 100644 udisks2-monitor-performance-230.patch create mode 100644 udisks2-monitor-performance-273.patch create mode 100644 udisks2-monitor-performance-290.patch diff --git a/gvfs.spec b/gvfs.spec index bb8444c..50397be 100644 --- a/gvfs.spec +++ b/gvfs.spec @@ -22,7 +22,7 @@ Name: gvfs Version: 1.48.1 -Release: 6%{?dist} +Release: 7%{?dist} Summary: Backends for the gio framework in GLib License: GPLv3 and LGPLv2+ and BSD and MPLv2.0 @@ -45,6 +45,12 @@ Patch7: smb-Disable-seek-support-when-appening.patch Patch8: smb-Fix-offset-after-truncate-when-appending.patch Patch9: smb-Implement-support-for-edit-mode.patch +# https://issues.redhat.com/browse/RHEL-143934 +Patch10: udisks2-monitor-performance-202.patch +Patch11: udisks2-monitor-performance-230.patch +Patch12: udisks2-monitor-performance-273.patch +Patch13: udisks2-monitor-performance-290.patch + BuildRequires: meson BuildRequires: gcc BuildRequires: pkgconfig @@ -441,6 +447,9 @@ killall -USR1 gvfsd >&/dev/null || : %{_datadir}/installed-tests %changelog +* Mon Jan 26 2026 Milan Crha - 1.48.1-7 +- udisks2: Use hash table for quicker lookups in the monitor (RHEL-143934) + * Thu Feb 13 2025 Ondrej Holy - 1.48.1-6 - Add edit mode support for smb backend (RHEL-71088) diff --git a/udisks2-monitor-performance-202.patch b/udisks2-monitor-performance-202.patch new file mode 100644 index 0000000..9f7f151 --- /dev/null +++ b/udisks2-monitor-performance-202.patch @@ -0,0 +1,109 @@ +From 14dc69defb82a789999ba43e5350438d9c7086a6 Mon Sep 17 00:00:00 2001 +Date: Mon, 11 Mar 2024 10:20:51 +0100 +Subject: [PATCH] udisks2: Do not schedule update if pending already + +Currently, the udisks2 volume monitor updates everything for each +`/proc/mounts`, `/etc/fstab`, and udisks2 change. The update takes some +time. If too many changes happen in a short timeframe, the udisks2 +volume monitor is not able to handle those events in a real-time. The +pending events can be processed for a long time. Many of those updates +are redundant though as the real changes were already reflected during +the previous updates. This causes a high CPU load among others. Let's +add small timeout that will allow to skip accumullated events. + +Fixes: https://gitlab.gnome.org/GNOME/gvfs/-/issues/713 +--- + monitor/udisks2/gvfsudisks2volumemonitor.c | 35 +++++++++++++++++++--- + 1 file changed, 31 insertions(+), 4 deletions(-) + +diff --git a/monitor/udisks2/gvfsudisks2volumemonitor.c b/monitor/udisks2/gvfsudisks2volumemonitor.c +index c8864aea8..540683b22 100644 +--- a/monitor/udisks2/gvfsudisks2volumemonitor.c ++++ b/monitor/udisks2/gvfsudisks2volumemonitor.c +@@ -68,8 +68,12 @@ struct _GVfsUDisks2VolumeMonitor + + GSettings *lockdown_settings; + gboolean readonly_lockdown; ++ ++ gint update_id; + }; + ++#define UPDATE_TIMEOUT 100 /* ms */ ++ + static UDisksClient *get_udisks_client_sync (GError **error); + + static void update_all (GVfsUDisks2VolumeMonitor *monitor, +@@ -148,6 +152,8 @@ gvfs_udisks2_volume_monitor_finalize (GObject *object) + + g_clear_object (&monitor->lockdown_settings); + ++ g_clear_handle_id (&monitor->update_id, g_source_remove); ++ + G_OBJECT_CLASS (gvfs_udisks2_volume_monitor_parent_class)->finalize (object); + } + +@@ -425,12 +431,33 @@ gvfs_udisks2_volume_monitor_get_readonly_lockdown (GVfsUDisks2VolumeMonitor *mon + + /* ---------------------------------------------------------------------------------------------------- */ + ++static gboolean ++update_func (gpointer user_data) ++{ ++ GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (user_data); ++ ++ monitor->update_id = 0; ++ ++ update_all (monitor, TRUE, FALSE); ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++schedule_update (GVfsUDisks2VolumeMonitor *monitor) ++{ ++ if (monitor->update_id != 0) ++ return; ++ ++ monitor->update_id = g_timeout_add (UPDATE_TIMEOUT, update_func, monitor); ++} ++ + void + gvfs_udisks2_volume_monitor_update (GVfsUDisks2VolumeMonitor *monitor) + { + g_return_if_fail (GVFS_IS_UDISKS2_VOLUME_MONITOR (monitor)); + udisks_client_settle (monitor->client); +- update_all (monitor, TRUE, FALSE); ++ schedule_update (monitor); + } + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -532,7 +559,7 @@ on_client_changed (UDisksClient *client, + gpointer user_data) + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (user_data); +- update_all (monitor, TRUE, FALSE); ++ schedule_update (monitor); + } + + static void +@@ -540,7 +567,7 @@ mountpoints_changed (GUnixMountMonitor *mount_monitor, + gpointer user_data) + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (user_data); +- update_all (monitor, TRUE, FALSE); ++ schedule_update (monitor); + } + + static void +@@ -548,7 +575,7 @@ mounts_changed (GUnixMountMonitor *mount_monitor, + gpointer user_data) + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (user_data); +- update_all (monitor, TRUE, FALSE); ++ schedule_update (monitor); + } + + /* ---------------------------------------------------------------------------------------------------- */ +-- +GitLab + diff --git a/udisks2-monitor-performance-230.patch b/udisks2-monitor-performance-230.patch new file mode 100644 index 0000000..746eecb --- /dev/null +++ b/udisks2-monitor-performance-230.patch @@ -0,0 +1,39 @@ +From 45a5612574a54bf4648cedafa5b977efb48293c9 Mon Sep 17 00:00:00 2001 +Date: Thu, 12 Sep 2024 13:27:04 +0200 +Subject: [PATCH] udisks2: Run update synchronously for internal changes + +The commit 14dc69de introduced performance improvement by skipping +accumulated mount, mount point and udisks2 client changes. That was +achieved by running the update with a small delay. But this logic was +mistakenly used for the `gvfs_udisks2_volume_monitor_update` function +also. This function expects the update to happen synchronously. This +leads to various issues e.g. `g_volume_get_mount` fails when called +immediately after `g_volume_mount`. Let's run the update synchronously +from the `gvfs_udisks2_volume_monitor_update` function to fix the +mentioned issues. + +Fixes: https://gitlab.gnome.org/GNOME/gvfs/-/issues/762 +--- + monitor/udisks2/gvfsudisks2volumemonitor.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/monitor/udisks2/gvfsudisks2volumemonitor.c b/monitor/udisks2/gvfsudisks2volumemonitor.c +index 3a2015b29..f78f28d40 100644 +--- a/monitor/udisks2/gvfsudisks2volumemonitor.c ++++ b/monitor/udisks2/gvfsudisks2volumemonitor.c +@@ -457,7 +457,11 @@ gvfs_udisks2_volume_monitor_update (GVfsUDisks2VolumeMonitor *monitor) + { + g_return_if_fail (GVFS_IS_UDISKS2_VOLUME_MONITOR (monitor)); + udisks_client_settle (monitor->client); +- schedule_update (monitor); ++ ++ if (monitor->update_id != 0) ++ g_source_remove (monitor->update_id); ++ ++ update_func (monitor); + } + + /* ---------------------------------------------------------------------------------------------------- */ +-- +GitLab + diff --git a/udisks2-monitor-performance-273.patch b/udisks2-monitor-performance-273.patch new file mode 100644 index 0000000..18dc303 --- /dev/null +++ b/udisks2-monitor-performance-273.patch @@ -0,0 +1,1741 @@ +Squashed and adapted commits from https://gitlab.gnome.org/GNOME/gvfs/-/merge_requests/273 + +diff --git a/monitor/udisks2/gvfsudisks2volumemonitor.c b/monitor/udisks2/gvfsudisks2volumemonitor.c +index bfed054..d89c0a0 100644 +--- a/monitor/udisks2/gvfsudisks2volumemonitor.c ++++ b/monitor/udisks2/gvfsudisks2volumemonitor.c +@@ -58,13 +58,15 @@ struct _GVfsUDisks2VolumeMonitor + GUdevClient *gudev_client; + GUnixMountMonitor *mount_monitor; + +- GList *drives; +- GList *volumes; +- GList *fstab_volumes; +- GList *mounts; ++ GHashTable *drives_by_udisks_drive; /* UDisksDrive * ~> GVfsUDisks2Drive * */ ++ GHashTable *volumes; /* GVfsUDisks2Volume * ~> guint64 *dev_id */ ++ GHashTable *volumes_by_dev_id; /* guint64 *dev_id ~> GVfsUDisks2Volume * */ ++ GHashTable *fstab_volumes; /* GVfsUDisks2Volume * */ ++ GHashTable *mounts; /* gchar *path ~> GVfsUDisks2Mount * */ + /* we keep volumes/mounts for blank and audio discs separate to handle e.g. mixed discs properly */ +- GList *disc_volumes; +- GList *disc_mounts; ++ GHashTable *disc_volumes; /* GVfsUDisks2Volume * ~> guint64 *dev_id */ ++ GHashTable *disc_volumes_by_dev_id; /* guint64 *dev_id ~> GVfsUDisks2Volume * */ ++ GHashTable *disc_mounts; /* GVfsUDisks2Mount * */ + + GSettings *lockdown_settings; + gboolean readonly_lockdown; +@@ -84,6 +86,8 @@ static void update_drives (GVfsUDisks2VolumeMonitor *monitor, + GList **removed_drives, + gboolean coldplug); + static void update_volumes (GVfsUDisks2VolumeMonitor *monitor, ++ GHashTable *mount_entries, ++ GHashTable *mount_points_by_path, + GList **added_volumes, + GList **removed_volumes, + gboolean coldplug); +@@ -92,6 +96,8 @@ static void update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + GList **removed_volumes, + gboolean coldplug); + static void update_mounts (GVfsUDisks2VolumeMonitor *monitor, ++ GHashTable *mount_entries, ++ GHashTable *mount_points_by_path, /* gchar *path ~> GUnixMountPoint * */ + GList **added_mounts, + GList **removed_mounts, + gboolean coldplug); +@@ -114,6 +120,123 @@ static void mounts_changed (GUnixMountMonitor *mount_monitor, + + G_DEFINE_TYPE (GVfsUDisks2VolumeMonitor, gvfs_udisks2_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR) + ++static guint64 * ++gvfs_dev_id_new (dev_t dev_id) ++{ ++ guint64 *id = g_new0 (guint64, 1); ++ *id = (guint64) dev_id; ++ return id; ++} ++ ++/* It removes the @volume from @volumes_by_dev_id and if the @volumes ++ * contains a different volume with the same dev_id, then it's added ++ * into the @volumes_by_dev_id. It does not modify the @volumes itself. ++ */ ++static void ++gvfs_remove_volume_with_dev_id (GHashTable *volumes_by_dev_id, /* guint64 *(dev_id) ~> GVfsUDisks2Volume * */ ++ GVfsUDisks2Volume *volume) ++{ ++ GVfsUDisks2Volume *existing_volume; ++ guint64 dev_id = (guint64) gvfs_udisks2_volume_get_dev (volume); ++ ++ if (dev_id == 0) ++ return; ++ ++ existing_volume = g_hash_table_lookup (volumes_by_dev_id, &dev_id); ++ if (existing_volume == volume) ++ g_hash_table_remove (volumes_by_dev_id, &dev_id); ++} ++ ++static void ++volume_changed (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Volume *volume, ++ gpointer user_data) ++{ ++ GHashTable *volumes = NULL; ++ GHashTable *volumes_by_dev_id = NULL; ++ guint64 *pdev_id; ++ ++ /* in case something else invokes the signal with a different volume instance type */ ++ if (!GVFS_IS_UDISKS2_VOLUME (volume)) ++ return; ++ ++ pdev_id = g_hash_table_lookup (monitor->volumes, volume); ++ if (pdev_id != NULL) ++ { ++ volumes = monitor->volumes; ++ volumes_by_dev_id = monitor->volumes_by_dev_id; ++ } ++ else ++ { ++ pdev_id = g_hash_table_lookup (monitor->disc_volumes, volume); ++ if (pdev_id != NULL) ++ { ++ volumes = monitor->disc_volumes; ++ volumes_by_dev_id = monitor->disc_volumes_by_dev_id; ++ } ++ } ++ ++ /* it had been found in one of the volume hashes */ ++ if (volumes_by_dev_id != NULL) ++ { ++ dev_t new_dev_id = gvfs_udisks2_volume_get_dev (volume); ++ /* update the dev_id information, if it changed */ ++ if (*pdev_id != (guint64) new_dev_id) ++ { ++ guint64 stack_dev_id = *pdev_id; ++ ++ g_object_ref (volume); ++ g_hash_table_remove (volumes_by_dev_id, &stack_dev_id); ++ g_hash_table_insert (volumes, g_object_ref (volume), gvfs_dev_id_new (new_dev_id)); ++ if (new_dev_id != 0) ++ g_hash_table_insert (volumes_by_dev_id, gvfs_dev_id_new (new_dev_id), g_object_ref (volume)); ++ g_object_unref (volume); ++ } ++ } ++} ++ ++/* this mimics g_str_hash(), except it ignores the trailing slash in the path */ ++static guint ++gvfs_mount_path_str_hash (gconstpointer ptr) ++{ ++ const signed char *path = ptr; ++ guint value = 5381; ++ ++ if (path == NULL || *path == '\0') ++ return value; ++ ++ while (*path != '\0') ++ { ++ /* ignore trailing slash */ ++ if (*path == '/' && path[1] == '\0') ++ break; ++ value = (value << 5) + value + (*path); ++ path++; ++ } ++ ++ return value; ++} ++ ++static gboolean ++gvfs_mount_path_str_equal (gconstpointer ptr1, ++ gconstpointer ptr2) ++{ ++ const gchar *path1 = ptr1, *path2 = ptr2; ++ ++ if (path1 == NULL || path2 == NULL) ++ return path1 == path2; ++ ++ while (*path1 != '\0' && *path2 != '\0' && *path1 == *path2) ++ { ++ path1++; ++ path2++; ++ } ++ ++ /* ignore trailing slash */ ++ return (*path1 == '\0' && (*path2 == '\0' || (*path2 == '/' && path2[1] == '\0'))) || ++ (*path2 == '\0' && (*path1 == '\0' || (*path1 == '/' && path1[1] == '\0'))); ++} ++ + static void + gvfs_udisks2_volume_monitor_dispose (GObject *object) + { +@@ -139,13 +262,15 @@ gvfs_udisks2_volume_monitor_finalize (GObject *object) + g_clear_object (&monitor->client); + g_clear_object (&monitor->gudev_client); + +- g_list_free_full (monitor->drives, g_object_unref); +- g_list_free_full (monitor->volumes, g_object_unref); +- g_list_free_full (monitor->fstab_volumes, g_object_unref); +- g_list_free_full (monitor->mounts, g_object_unref); ++ g_clear_pointer (&monitor->drives_by_udisks_drive, g_hash_table_unref); ++ g_clear_pointer (&monitor->volumes, g_hash_table_unref); ++ g_clear_pointer (&monitor->volumes_by_dev_id, g_hash_table_unref); ++ g_clear_pointer (&monitor->fstab_volumes, g_hash_table_unref); ++ g_clear_pointer (&monitor->mounts, g_hash_table_unref); + +- g_list_free_full (monitor->disc_volumes, g_object_unref); +- g_list_free_full (monitor->disc_mounts, g_object_unref); ++ g_clear_pointer (&monitor->disc_volumes, g_hash_table_unref); ++ g_clear_pointer (&monitor->disc_volumes_by_dev_id, g_hash_table_unref); ++ g_clear_pointer (&monitor->disc_mounts, g_hash_table_unref); + + g_clear_object (&monitor->lockdown_settings); + +@@ -158,11 +283,22 @@ static GList * + get_mounts (GVolumeMonitor *_monitor) + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (_monitor); +- GList *ret; ++ GList *ret = NULL; ++ GHashTableIter iter; ++ gpointer key = NULL, value = NULL; + +- ret = g_list_copy (monitor->mounts); +- ret = g_list_concat (ret, g_list_copy (monitor->disc_mounts)); +- g_list_foreach (ret, (GFunc) g_object_ref, NULL); ++ g_hash_table_iter_init (&iter, monitor->mounts); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) ++ { ++ GVfsUDisks2Mount *mount = value; ++ ret = g_list_prepend (ret, g_object_ref (mount)); ++ } ++ g_hash_table_iter_init (&iter, monitor->disc_mounts); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) ++ { ++ GVfsUDisks2Mount *mount = key; ++ ret = g_list_prepend (ret, g_object_ref (mount)); ++ } + return ret; + } + +@@ -170,12 +306,25 @@ static GList * + get_volumes (GVolumeMonitor *_monitor) + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (_monitor); +- GList *ret; ++ GHashTableIter iter; ++ gpointer key = NULL; ++ GList *ret = NULL; + +- ret = g_list_copy (monitor->volumes); +- ret = g_list_concat (ret, g_list_copy (monitor->fstab_volumes)); +- ret = g_list_concat (ret, g_list_copy (monitor->disc_volumes)); +- g_list_foreach (ret, (GFunc) g_object_ref, NULL); ++ g_hash_table_iter_init (&iter, monitor->volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) ++ { ++ ret = g_list_prepend (ret, g_object_ref (key)); ++ } ++ g_hash_table_iter_init (&iter, monitor->fstab_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) ++ { ++ ret = g_list_prepend (ret, g_object_ref (key)); ++ } ++ g_hash_table_iter_init (&iter, monitor->disc_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) ++ { ++ ret = g_list_prepend (ret, g_object_ref (key)); ++ } + return ret; + } + +@@ -183,10 +332,16 @@ static GList * + get_connected_drives (GVolumeMonitor *_monitor) + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (_monitor); +- GList *ret; ++ GList *ret = NULL; ++ GHashTableIter iter; ++ gpointer value = NULL; + +- ret = g_list_copy (monitor->drives); +- g_list_foreach (ret, (GFunc) g_object_ref, NULL); ++ g_hash_table_iter_init (&iter, monitor->drives_by_udisks_drive); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) ++ { ++ GVfsUDisks2Drive *drive = value; ++ ret = g_list_prepend (ret, g_object_ref (drive)); ++ } + return ret; + } + +@@ -196,23 +351,27 @@ get_volume_for_uuid (GVolumeMonitor *_monitor, + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (_monitor); + GVfsUDisks2Volume *volume; +- GList *l; ++ GHashTableIter iter; ++ gpointer key = NULL; + +- for (l = monitor->volumes; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, monitor->volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- volume = l->data; +- if (gvfs_udisks2_volume_has_uuid (l->data, uuid)) ++ volume = key; ++ if (gvfs_udisks2_volume_has_uuid (volume, uuid)) + goto found; + } +- for (l = monitor->fstab_volumes; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, monitor->fstab_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- volume = l->data; +- if (gvfs_udisks2_volume_has_uuid (l->data, uuid)) ++ volume = key; ++ if (gvfs_udisks2_volume_has_uuid (volume, uuid)) + goto found; + } +- for (l = monitor->disc_volumes; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, monitor->disc_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- volume = l->data; ++ volume = key; + if (gvfs_udisks2_volume_has_uuid (volume, uuid)) + goto found; + } +@@ -229,17 +388,20 @@ get_mount_for_uuid (GVolumeMonitor *_monitor, + { + GVfsUDisks2VolumeMonitor *monitor = GVFS_UDISKS2_VOLUME_MONITOR (_monitor); + GVfsUDisks2Mount *mount; +- GList *l; ++ GHashTableIter iter; ++ gpointer key = NULL, value = NULL; + +- for (l = monitor->mounts; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, monitor->mounts); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { +- mount = l->data; +- if (gvfs_udisks2_mount_has_uuid (l->data, uuid)) ++ mount = value; ++ if (gvfs_udisks2_mount_has_uuid (mount, uuid)) + goto found; + } +- for (l = monitor->disc_mounts; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, monitor->disc_mounts); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- mount = l->data; ++ mount = key; + if (gvfs_udisks2_mount_has_uuid (mount, uuid)) + goto found; + } +@@ -250,6 +412,13 @@ get_mount_for_uuid (GVolumeMonitor *_monitor, + return G_MOUNT (g_object_ref (mount)); + } + ++static GVfsUDisks2Mount * ++find_mount_by_mount_path (GVfsUDisks2VolumeMonitor *monitor, ++ const gchar *mount_path) ++{ ++ return g_hash_table_lookup (monitor->mounts, mount_path); ++} ++ + static GMount * + get_mount_for_mount_path (const gchar *mount_path, + GCancellable *cancellable) +@@ -272,19 +441,14 @@ get_mount_for_mount_path (const gchar *mount_path, + /* creation of the volume monitor could fail */ + if (monitor != NULL) + { +- GList *l; +- for (l = monitor->mounts; l != NULL; l = l->next) ++ GVfsUDisks2Mount *mount = find_mount_by_mount_path (monitor, mount_path); ++ ++ if (mount != NULL) + { +- GVfsUDisks2Mount *mount = GVFS_UDISKS2_MOUNT (l->data); +- if (g_strcmp0 (gvfs_udisks2_mount_get_mount_path (mount), mount_path) == 0) +- { +- ret = G_MOUNT (g_object_ref (mount)); +- goto out; +- } ++ ret = G_MOUNT (g_object_ref (mount)); + } + } + +- out: + if (monitor != NULL) + g_object_unref (monitor); + return ret; +@@ -329,6 +493,15 @@ lockdown_settings_changed (GSettings *settings, + static void + gvfs_udisks2_volume_monitor_init (GVfsUDisks2VolumeMonitor *monitor) + { ++ monitor->drives_by_udisks_drive = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); ++ monitor->volumes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_free); ++ monitor->volumes_by_dev_id = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_object_unref); ++ monitor->fstab_volumes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); ++ monitor->mounts = g_hash_table_new_full (gvfs_mount_path_str_hash, gvfs_mount_path_str_equal, g_free, g_object_unref); ++ monitor->disc_volumes = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, g_free); ++ monitor->disc_volumes_by_dev_id = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_object_unref); ++ monitor->disc_mounts = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL); ++ + monitor->gudev_client = g_udev_client_new (NULL); /* don't listen to any changes */ + + monitor->client = get_udisks_client_sync (NULL); +@@ -346,6 +519,10 @@ gvfs_udisks2_volume_monitor_init (GVfsUDisks2VolumeMonitor *monitor) + "mountpoints-changed", + G_CALLBACK (mountpoints_changed), + monitor); ++ g_signal_connect (monitor, ++ "volume-changed", ++ G_CALLBACK (volume_changed), ++ monitor); + + monitor->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown"); + monitor->readonly_lockdown = g_settings_get_boolean (monitor->lockdown_settings, +@@ -484,57 +661,6 @@ get_udisks_client_sync (GError **error) + + /* ---------------------------------------------------------------------------------------------------- */ + +-static void +-diff_sorted_lists (GList *list1, +- GList *list2, +- GCompareFunc compare, +- GList **added, +- GList **removed, +- GList **unchanged) +-{ +- int order; +- +- *added = *removed = NULL; +- if (unchanged != NULL) +- *unchanged = NULL; +- +- while (list1 != NULL && +- list2 != NULL) +- { +- order = (*compare) (list1->data, list2->data); +- if (order < 0) +- { +- *removed = g_list_prepend (*removed, list1->data); +- list1 = list1->next; +- } +- else if (order > 0) +- { +- *added = g_list_prepend (*added, list2->data); +- list2 = list2->next; +- } +- else +- { /* same item */ +- if (unchanged != NULL) +- *unchanged = g_list_prepend (*unchanged, list1->data); +- list1 = list1->next; +- list2 = list2->next; +- } +- } +- +- while (list1 != NULL) +- { +- *removed = g_list_prepend (*removed, list1->data); +- list1 = list1->next; +- } +- while (list2 != NULL) +- { +- *added = g_list_prepend (*added, list2->data); +- list2 = list2->next; +- } +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ +- + static void + object_list_emit (GVfsUDisks2VolumeMonitor *monitor, + const gchar *monitor_signal, +@@ -586,6 +712,33 @@ update_all (GVfsUDisks2VolumeMonitor *monitor, + GList *added_drives, *removed_drives; + GList *added_volumes, *removed_volumes; + GList *added_mounts, *removed_mounts; ++ GList *entries, *points, *link; ++ GHashTable *mount_entries; /* gchar *path ~> GUnixMountEntry * */ ++ GHashTable *mount_points_by_path; /* gchar *path ~> GUnixMountPoint * */ ++ ++ mount_entries = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_unix_mount_free); ++ ++ /* move mount entries into a hash table */ ++ entries = g_unix_mounts_get (NULL); ++ for (link = entries; link != NULL; link = g_list_next (link)) ++ { ++ GUnixMountEntry *mount_entry = link->data; ++ g_hash_table_insert (mount_entries, (gpointer) g_unix_mount_get_mount_path (mount_entry), mount_entry); ++ } ++ /* the mount_entries took ownership of the mount entry objects */ ++ g_list_free (entries); ++ ++ mount_points_by_path = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_unix_mount_point_free); ++ ++ /* move mount points into a hash table */ ++ points = g_unix_mount_points_get (NULL); ++ for (link = points; link != NULL; link = g_list_next (link)) ++ { ++ GUnixMountPoint *mount_point = link->data; ++ g_hash_table_insert (mount_points_by_path, (gpointer) g_unix_mount_point_get_mount_path (mount_point), mount_point); ++ } ++ /* the mount_points_by_path took ownership of the mount point objects */ ++ g_list_free (points); + + added_drives = NULL; + removed_drives = NULL; +@@ -595,9 +748,9 @@ update_all (GVfsUDisks2VolumeMonitor *monitor, + removed_mounts = NULL; + + update_drives (monitor, &added_drives, &removed_drives, coldplug); +- update_volumes (monitor, &added_volumes, &removed_volumes, coldplug); ++ update_volumes (monitor, mount_entries, mount_points_by_path, &added_volumes, &removed_volumes, coldplug); + update_fstab_volumes (monitor, &added_volumes, &removed_volumes, coldplug); +- update_mounts (monitor, &added_mounts, &removed_mounts, coldplug); ++ update_mounts (monitor, mount_entries, mount_points_by_path, &added_mounts, &removed_mounts, coldplug); + update_discs (monitor, + &added_volumes, &removed_volumes, + &added_mounts, &removed_mounts, +@@ -633,6 +786,8 @@ update_all (GVfsUDisks2VolumeMonitor *monitor, + g_list_free_full (added_volumes, g_object_unref); + g_list_free_full (removed_mounts, g_object_unref); + g_list_free_full (added_mounts, g_object_unref); ++ g_hash_table_unref (mount_entries); ++ g_hash_table_unref (mount_points_by_path); + } + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -729,21 +884,23 @@ should_include_mount_point (GVfsUDisks2VolumeMonitor *monitor, + + static gboolean + should_include_mount (GVfsUDisks2VolumeMonitor *monitor, +- GUnixMountEntry *mount_entry) ++ GUnixMountEntry *mount_entry, ++ GHashTable *mount_points_by_path) /* gchar *path ~> GUnixMountPoint * */ + { + GUnixMountPoint *mount_point; + const gchar *options; ++ const gchar *mount_path; + gboolean ret; + + /* If mounted at the designated mount point, use g_unix_mount_point_get_options + * in prior to g_unix_mount_get_options to keep support of "comment=" options, + * see https://gitlab.gnome.org/GNOME/gvfs/issues/348. + */ +- mount_point = g_unix_mount_point_at (g_unix_mount_get_mount_path (mount_entry), NULL); ++ mount_path = g_unix_mount_get_mount_path (mount_entry); ++ mount_point = g_hash_table_lookup (mount_points_by_path, mount_path); + if (mount_point != NULL) + { + ret = should_include_mount_point (monitor, mount_point); +- g_unix_mount_point_free (mount_point); + goto out; + } + +@@ -753,12 +910,12 @@ should_include_mount (GVfsUDisks2VolumeMonitor *monitor, + options = g_unix_mount_get_options (mount_entry); + if (options != NULL) + { +- ret = should_include (g_unix_mount_get_mount_path (mount_entry), ++ ret = should_include (mount_path, + options); + goto out; + } + +- ret = should_include (g_unix_mount_get_mount_path (mount_entry), NULL); ++ ret = should_include (mount_path, NULL); + + out: + return ret; +@@ -769,7 +926,9 @@ should_include_mount (GVfsUDisks2VolumeMonitor *monitor, + + static gboolean + should_include_volume_check_mount_points (GVfsUDisks2VolumeMonitor *monitor, +- UDisksBlock *block) ++ UDisksBlock *block, ++ GHashTable *mount_entries, /* gchar *path ~> GUnixMountEntry * */ ++ GHashTable *mount_points_by_path) /* gchar *path ~> GUnixMountPoint * */ + { + gboolean ret = TRUE; + GDBusObject *obj; +@@ -791,7 +950,7 @@ should_include_volume_check_mount_points (GVfsUDisks2VolumeMonitor *monitor, + const gchar *mount_point = mount_points[n]; + GUnixMountEntry *mount_entry; + +- mount_entry = g_unix_mount_at (mount_point, NULL); ++ mount_entry = g_hash_table_lookup (mount_entries, mount_point); + if (mount_entry != NULL) + { + const gchar *root = NULL; +@@ -801,15 +960,13 @@ should_include_volume_check_mount_points (GVfsUDisks2VolumeMonitor *monitor, + #endif + + if ((root == NULL || g_strcmp0 (root, "/") == 0) && +- should_include_mount (monitor, mount_entry)) ++ should_include_mount (monitor, mount_entry, mount_points_by_path)) + { +- g_unix_mount_free (mount_entry); + ret = TRUE; + goto out; + } + + ret = FALSE; +- g_unix_mount_free (mount_entry); + } + } + +@@ -857,6 +1014,8 @@ static gboolean should_include_drive (GVfsUDisks2VolumeMonitor *monitor, + static gboolean + should_include_volume (GVfsUDisks2VolumeMonitor *monitor, + UDisksBlock *block, ++ GHashTable *mount_entries, /* gchar *path ~> GUnixMountEntry * */ ++ GHashTable *mount_points_by_path, /* gchar *path ~> GUnixMountPoint * */ + gboolean allow_encrypted_cleartext) + { + gboolean ret = FALSE; +@@ -917,7 +1076,7 @@ should_include_volume (GVfsUDisks2VolumeMonitor *monitor, + cleartext_block = udisks_client_get_cleartext_block (monitor->client, block); + if (cleartext_block != NULL) + { +- ret = should_include_volume (monitor, cleartext_block, TRUE); ++ ret = should_include_volume (monitor, cleartext_block, mount_entries, mount_points_by_path, TRUE); + g_object_unref (cleartext_block); + } + else +@@ -942,7 +1101,7 @@ should_include_volume (GVfsUDisks2VolumeMonitor *monitor, + * is mounted in a place where the mount is to be ignored, we ignore the volume + * as well + */ +- if (!should_include_volume_check_mount_points (monitor, block)) ++ if (!should_include_volume_check_mount_points (monitor, block, mount_entries, mount_points_by_path)) + goto out; + + object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block)); +@@ -1003,47 +1162,23 @@ should_include_drive (GVfsUDisks2VolumeMonitor *monitor, + + /* ---------------------------------------------------------------------------------------------------- */ + +-static gint +-udisks_drive_compare (UDisksDrive *a, UDisksDrive *b) ++static void ++add_disc_volume (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Volume *volume) + { +- GDBusObject *oa = g_dbus_interface_get_object (G_DBUS_INTERFACE (a)); +- GDBusObject *ob = g_dbus_interface_get_object (G_DBUS_INTERFACE (b)); +- /* Either or both of oa, ob can be NULL for the case where a drive +- * is removed but we still hold a reference to the drive interface +- */ +- if (oa != NULL && ob != NULL) +- return g_strcmp0 (g_dbus_object_get_object_path (oa), g_dbus_object_get_object_path (ob)); +- else +- return (const gchar*) ob - (const gchar*) oa; +-} ++ dev_t dev_id = gvfs_udisks2_volume_get_dev (volume); + +-static gint +-block_compare (UDisksBlock *a, UDisksBlock *b) +-{ +- return g_strcmp0 (udisks_block_get_device (a), udisks_block_get_device (b)); ++ g_hash_table_insert (monitor->disc_volumes, g_object_ref (volume), gvfs_dev_id_new (dev_id)); ++ if (dev_id != 0) ++ g_hash_table_insert (monitor->disc_volumes_by_dev_id, gvfs_dev_id_new (dev_id), g_object_ref (volume)); + } + +-/* ---------------------------------------------------------------------------------------------------- */ +- +-static GVfsUDisks2Volume * +-find_disc_volume_for_block (GVfsUDisks2VolumeMonitor *monitor, +- UDisksBlock *block) ++static void ++remove_disc_volume (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Volume *volume) + { +- GVfsUDisks2Volume *ret = NULL; +- GList *l; +- +- for (l = monitor->disc_volumes; l != NULL; l = l->next) +- { +- GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (l->data); +- if (gvfs_udisks2_volume_get_block (volume) == block) +- { +- ret = volume; +- goto out; +- } +- } +- +- out: +- return ret; ++ gvfs_remove_volume_with_dev_id (monitor->disc_volumes_by_dev_id, volume); ++ g_hash_table_remove (monitor->disc_volumes, volume); + } + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -1052,67 +1187,63 @@ static GVfsUDisks2Drive * + find_drive_for_udisks_drive (GVfsUDisks2VolumeMonitor *monitor, + UDisksDrive *udisks_drive) + { +- GVfsUDisks2Drive *ret = NULL; +- GList *l; ++ return g_hash_table_lookup (monitor->drives_by_udisks_drive, udisks_drive); ++} + +- for (l = monitor->drives; l != NULL; l = l->next) +- { +- GVfsUDisks2Drive *drive = GVFS_UDISKS2_DRIVE (l->data); +- if (gvfs_udisks2_drive_get_udisks_drive (drive) == udisks_drive) +- { +- ret = drive; +- goto out; +- } +- } ++static void ++add_drive (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Drive *drive) ++{ ++ g_hash_table_insert (monitor->drives_by_udisks_drive, gvfs_udisks2_drive_get_udisks_drive (drive), g_object_ref (drive)); ++} + +- out: +- return ret; ++static void ++remove_drive (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Drive *drive) ++{ ++ g_hash_table_remove (monitor->drives_by_udisks_drive, gvfs_udisks2_drive_get_udisks_drive (drive)); + } + + /* ---------------------------------------------------------------------------------------------------- */ + +-static GVfsUDisks2Volume * +-find_volume_for_block (GVfsUDisks2VolumeMonitor *monitor, +- UDisksBlock *block) ++static void ++add_volume (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Volume *volume) + { +- GVfsUDisks2Volume *ret = NULL; +- GList *l; ++ dev_t dev_id = gvfs_udisks2_volume_get_dev (volume); + +- for (l = monitor->volumes; l != NULL; l = l->next) +- { +- GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (l->data); +- if (gvfs_udisks2_volume_get_block (volume) == block) +- { +- ret = volume; +- goto out; +- } +- } +- +- out: +- return ret; ++ g_hash_table_insert (monitor->volumes, g_object_ref (volume), gvfs_dev_id_new (dev_id)); ++ if (dev_id != 0) ++ g_hash_table_insert (monitor->volumes_by_dev_id, gvfs_dev_id_new (dev_id), g_object_ref (volume)); + } + + /* ---------------------------------------------------------------------------------------------------- */ + +-static GVfsUDisks2Volume * +-find_fstab_volume_for_mount_point (GVfsUDisks2VolumeMonitor *monitor, +- GUnixMountPoint *mount_point) ++static void ++remove_volume (GVfsUDisks2VolumeMonitor *monitor, ++ GVfsUDisks2Volume *volume) + { +- GVfsUDisks2Volume *ret = NULL; +- GList *l; ++ gvfs_remove_volume_with_dev_id (monitor->volumes_by_dev_id, volume); ++ g_hash_table_remove (monitor->volumes, volume); ++} + +- for (l = monitor->fstab_volumes; l != NULL; l = l->next) +- { +- GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (l->data); +- if (g_unix_mount_point_compare (gvfs_udisks2_volume_get_mount_point (volume), mount_point) == 0) +- { +- ret = volume; +- goto out; +- } +- } ++/* ---------------------------------------------------------------------------------------------------- */ + +- out: +- return ret; ++static guint ++gvfs_mount_point_hash (gconstpointer ptr) ++{ ++ GUnixMountPoint *mount_point = (GUnixMountPoint *) ptr; ++ return g_str_hash (g_unix_mount_point_get_mount_path (mount_point)) ^ ++ g_str_hash (g_unix_mount_point_get_device_path (mount_point)); ++} ++ ++static gboolean ++gvfs_mount_point_equal (gconstpointer ptr1, ++ gconstpointer ptr2) ++{ ++ GUnixMountPoint *mount_point1 = (GUnixMountPoint *) ptr1; ++ GUnixMountPoint *mount_point2 = (GUnixMountPoint *) ptr2; ++ return g_unix_mount_point_compare (mount_point1, mount_point2) == 0; + } + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -1121,27 +1252,13 @@ static gboolean + mount_point_matches_mount_entry (GUnixMountPoint *mount_point, + GUnixMountEntry *mount_entry) + { +- gboolean ret = FALSE; +- gchar *mp_path = NULL; +- gchar *mp_entry = NULL; +- +- mp_path = g_strdup (g_unix_mount_point_get_mount_path (mount_point)); +- mp_entry = g_strdup (g_unix_mount_get_mount_path (mount_entry)); +- +- if (g_str_has_suffix (mp_path, "/")) +- mp_path[strlen(mp_path) - 1] = '\0'; +- if (g_str_has_suffix (mp_entry, "/")) +- mp_entry[strlen(mp_entry) - 1] = '\0'; +- +- if (g_strcmp0 (mp_path, mp_entry) != 0) +- goto out; ++ const gchar *mp_path; ++ const gchar *mp_entry; + +- ret = TRUE; ++ mp_path = g_unix_mount_point_get_mount_path (mount_point); ++ mp_entry = g_unix_mount_get_mount_path (mount_entry); + +- out: +- g_free (mp_path); +- g_free (mp_entry); +- return ret; ++ return gvfs_mount_path_str_equal (mp_path, mp_entry); + } + + static GVfsUDisks2Volume * +@@ -1149,11 +1266,13 @@ find_fstab_volume_for_mount_entry (GVfsUDisks2VolumeMonitor *monitor, + GUnixMountEntry *mount_entry) + { + GVfsUDisks2Volume *ret = NULL; +- GList *l; ++ GHashTableIter iter; ++ gpointer key = NULL; + +- for (l = monitor->fstab_volumes; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, monitor->fstab_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (l->data); ++ GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (key); + if (mount_point_matches_mount_entry (gvfs_udisks2_volume_get_mount_point (volume), mount_entry)) + { + ret = volume; +@@ -1170,24 +1289,20 @@ find_lonely_mount_for_mount_point (GVfsUDisks2VolumeMonitor *monitor, + GUnixMountPoint *mount_point) + { + GVfsUDisks2Mount *ret = NULL; +- GList *l; ++ GVfsUDisks2Mount *mount; + +- for (l = monitor->mounts; l != NULL; l = l->next) ++ mount = g_hash_table_lookup (monitor->mounts, g_unix_mount_point_get_mount_path (mount_point)); ++ if (mount != NULL) + { +- GVfsUDisks2Mount *mount = GVFS_UDISKS2_MOUNT (l->data); +- if (mount_point != NULL && +- mount_point_matches_mount_entry (mount_point, gvfs_udisks2_mount_get_mount_entry (mount))) ++ GVolume *volume = g_mount_get_volume (G_MOUNT (mount)); ++ if (volume != NULL) + { +- GVolume *volume = g_mount_get_volume (G_MOUNT (mount)); +- if (volume != NULL) +- { +- g_object_unref (volume); +- } +- else +- { +- ret = mount; +- goto out; +- } ++ g_object_unref (volume); ++ } ++ else ++ { ++ ret = mount; ++ goto out; + } + } + +@@ -1234,8 +1349,8 @@ find_volume_for_device (GVfsUDisks2VolumeMonitor *monitor, + { + GVfsUDisks2Volume *ret = NULL; + GList *blocks = NULL; +- GList *l; + struct stat statbuf; ++ guint64 dev_id; + + /* don't consider e.g. network mounts */ + if (g_str_has_prefix (device, "LABEL=")) +@@ -1273,50 +1388,18 @@ find_volume_for_device (GVfsUDisks2VolumeMonitor *monitor, + if (stat (device, &statbuf) != 0) + goto out; + +- for (l = monitor->volumes; l != NULL; l = l->next) +- { +- GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (l->data); +- if (gvfs_udisks2_volume_get_dev (volume) == statbuf.st_rdev) +- { +- ret = volume; +- goto out; +- } +- } +- +- for (l = monitor->disc_volumes; l != NULL; l = l->next) +- { +- GVfsUDisks2Volume *volume = GVFS_UDISKS2_VOLUME (l->data); +- if (gvfs_udisks2_volume_get_dev (volume) == statbuf.st_rdev) +- { +- ret = volume; +- goto out; +- } +- } ++ dev_id = (guint64) statbuf.st_rdev; + +- out: +- g_list_free_full (blocks, g_object_unref); +- return ret; +-} +- +-/* ---------------------------------------------------------------------------------------------------- */ ++ ret = g_hash_table_lookup (monitor->volumes_by_dev_id, &dev_id); ++ if (ret != NULL) ++ goto out; + +-static GVfsUDisks2Mount * +-find_mount_by_mount_path (GVfsUDisks2VolumeMonitor *monitor, +- const gchar *mount_path) +-{ +- GVfsUDisks2Mount *ret = NULL; +- GList *l; ++ ret = g_hash_table_lookup (monitor->disc_volumes_by_dev_id, &dev_id); ++ if (ret != NULL) ++ goto out; + +- for (l = monitor->mounts; l != NULL; l = l->next) +- { +- GVfsUDisks2Mount *mount = GVFS_UDISKS2_MOUNT (l->data); +- if (g_strcmp0 (gvfs_udisks2_mount_get_mount_path (mount), mount_path) == 0) +- { +- ret = mount; +- goto out; +- } +- } + out: ++ g_list_free_full (blocks, g_object_unref); + return ret; + } + +@@ -1328,76 +1411,56 @@ update_drives (GVfsUDisks2VolumeMonitor *monitor, + GList **removed_drives, + gboolean coldplug) + { +- GList *cur_udisks_drives; +- GList *new_udisks_drives; +- GList *removed, *added; +- GList *l; ++ GHashTable *cur_udisks_drives; /* UDisksDrive * ~> GVfsUDisks2Drive * */ ++ GHashTableIter iter; ++ gpointer key = NULL, value = NULL; + GVfsUDisks2Drive *drive; +- GList *objects; ++ GList *objects, *l; + + objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (monitor->client)); + +- cur_udisks_drives = NULL; +- for (l = monitor->drives; l != NULL; l = l->next) ++ cur_udisks_drives = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); ++ ++ g_hash_table_iter_init (&iter, monitor->drives_by_udisks_drive); ++ while (g_hash_table_iter_next (&iter, &key, &value)) + { +- cur_udisks_drives = g_list_prepend (cur_udisks_drives, +- gvfs_udisks2_drive_get_udisks_drive (GVFS_UDISKS2_DRIVE (l->data))); ++ g_hash_table_insert (cur_udisks_drives, key, g_object_ref (GVFS_UDISKS2_DRIVE (value))); + } + + /* remove devices we want to ignore - we do it here so we get to reevaluate + * on the next update whether they should still be ignored + */ +- new_udisks_drives = NULL; + for (l = objects; l != NULL; l = l->next) + { + UDisksDrive *udisks_drive = udisks_object_peek_drive (UDISKS_OBJECT (l->data)); + if (udisks_drive == NULL) + continue; + if (should_include_drive (monitor, udisks_drive)) +- new_udisks_drives = g_list_prepend (new_udisks_drives, udisks_drive); +- } +- +- cur_udisks_drives = g_list_sort (cur_udisks_drives, (GCompareFunc) udisks_drive_compare); +- new_udisks_drives = g_list_sort (new_udisks_drives, (GCompareFunc) udisks_drive_compare); +- diff_sorted_lists (cur_udisks_drives, +- new_udisks_drives, (GCompareFunc) udisks_drive_compare, +- &added, &removed, NULL); +- +- for (l = removed; l != NULL; l = l->next) +- { +- UDisksDrive *udisks_drive = UDISKS_DRIVE (l->data); +- +- drive = find_drive_for_udisks_drive (monitor, udisks_drive); +- if (drive != NULL) +- { +- gvfs_udisks2_drive_disconnected (drive); +- monitor->drives = g_list_remove (monitor->drives, drive); +- *removed_drives = g_list_prepend (*removed_drives, g_object_ref (drive)); +- g_object_unref (drive); +- } +- } +- +- for (l = added; l != NULL; l = l->next) +- { +- UDisksDrive *udisks_drive = UDISKS_DRIVE (l->data); +- +- drive = find_drive_for_udisks_drive (monitor, udisks_drive); +- if (drive == NULL) + { +- drive = gvfs_udisks2_drive_new (monitor, udisks_drive, coldplug); +- if (udisks_drive != NULL) ++ /* not in currently known drives => add it */ ++ if (!g_hash_table_remove (cur_udisks_drives, udisks_drive)) + { +- monitor->drives = g_list_prepend (monitor->drives, drive); +- *added_drives = g_list_prepend (*added_drives, g_object_ref (drive)); ++ drive = gvfs_udisks2_drive_new (monitor, udisks_drive, coldplug); ++ if (drive != NULL) ++ { ++ add_drive (monitor, drive); ++ *added_drives = g_list_prepend (*added_drives, g_steal_pointer (&drive)); ++ } + } + } + } + +- g_list_free (added); +- g_list_free (removed); ++ /* which left are removed */ ++ g_hash_table_iter_init (&iter, cur_udisks_drives); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) ++ { ++ drive = value; ++ gvfs_udisks2_drive_disconnected (drive); ++ *removed_drives = g_list_prepend (*removed_drives, g_object_ref (drive)); ++ remove_drive (monitor, drive); ++ } + +- g_list_free (cur_udisks_drives); +- g_list_free (new_udisks_drives); ++ g_hash_table_unref (cur_udisks_drives); + + g_list_free_full (objects, g_object_unref); + } +@@ -1406,88 +1469,74 @@ update_drives (GVfsUDisks2VolumeMonitor *monitor, + + static void + update_volumes (GVfsUDisks2VolumeMonitor *monitor, ++ GHashTable *mount_entries, /* gchar *path ~> GUnixMountEntry * */ ++ GHashTable *mount_points_by_path, /* gchar *path ~> GUnixMountPoint * */ + GList **added_volumes, + GList **removed_volumes, + gboolean coldplug) + { +- GList *cur_block_volumes; +- GList *new_block_volumes; +- GList *removed, *added; +- GList *l; ++ GHashTable *cur_block_volumes; /* UDisksBlock * ~> GVfsUDisks2Volume * */ ++ GHashTableIter iter; ++ gpointer key = NULL, value = NULL; + GVfsUDisks2Volume *volume; +- GList *objects; ++ GList *objects, *l; + + objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (monitor->client)); + +- cur_block_volumes = NULL; +- for (l = monitor->volumes; l != NULL; l = l->next) ++ cur_block_volumes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); ++ ++ g_hash_table_iter_init (&iter, monitor->volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- cur_block_volumes = g_list_prepend (cur_block_volumes, +- gvfs_udisks2_volume_get_block (GVFS_UDISKS2_VOLUME (l->data))); ++ volume = key; ++ g_hash_table_insert (cur_block_volumes, gvfs_udisks2_volume_get_block (volume), g_object_ref (volume)); + } + +- new_block_volumes = NULL; + for (l = objects; l != NULL; l = l->next) + { + UDisksBlock *block = udisks_object_peek_block (UDISKS_OBJECT (l->data)); + if (block == NULL) + continue; +- if (should_include_volume (monitor, block, FALSE)) +- new_block_volumes = g_list_prepend (new_block_volumes, block); +- } +- +- cur_block_volumes = g_list_sort (cur_block_volumes, (GCompareFunc) block_compare); +- new_block_volumes = g_list_sort (new_block_volumes, (GCompareFunc) block_compare); +- diff_sorted_lists (cur_block_volumes, +- new_block_volumes, (GCompareFunc) block_compare, +- &added, &removed, NULL); +- +- for (l = removed; l != NULL; l = l->next) +- { +- UDisksBlock *block = UDISKS_BLOCK (l->data); +- volume = find_volume_for_block (monitor, block); +- if (volume != NULL) ++ if (should_include_volume (monitor, block, mount_entries, mount_points_by_path, FALSE)) + { +- gvfs_udisks2_volume_removed (volume); +- monitor->volumes = g_list_remove (monitor->volumes, volume); +- *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume)); +- g_object_unref (volume); ++ /* not in currently known volumes => add it */ ++ if (!g_hash_table_remove (cur_block_volumes, block)) ++ { ++ GVfsUDisks2Drive *drive = NULL; ++ UDisksDrive *udisks_drive; ++ ++ udisks_drive = udisks_client_get_drive_for_block (monitor->client, block); ++ if (udisks_drive != NULL) ++ { ++ drive = find_drive_for_udisks_drive (monitor, udisks_drive); ++ g_object_unref (udisks_drive); ++ } ++ volume = gvfs_udisks2_volume_new (monitor, ++ block, ++ NULL, /* mount_point */ ++ drive, ++ NULL, /* activation_root */ ++ coldplug); ++ if (volume != NULL) ++ { ++ add_volume (monitor, volume); ++ *added_volumes = g_list_prepend (*added_volumes, g_steal_pointer (&volume)); ++ } ++ } + } + } + +- for (l = added; l != NULL; l = l->next) ++ /* which left are removed */ ++ g_hash_table_iter_init (&iter, cur_block_volumes); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { +- UDisksBlock *block = UDISKS_BLOCK (l->data); +- volume = find_volume_for_block (monitor, block); +- if (volume == NULL) +- { +- GVfsUDisks2Drive *drive = NULL; +- UDisksDrive *udisks_drive; +- +- udisks_drive = udisks_client_get_drive_for_block (monitor->client, block); +- if (udisks_drive != NULL) +- { +- drive = find_drive_for_udisks_drive (monitor, udisks_drive); +- g_object_unref (udisks_drive); +- } +- volume = gvfs_udisks2_volume_new (monitor, +- block, +- NULL, /* mount_point */ +- drive, +- NULL, /* activation_root */ +- coldplug); +- if (volume != NULL) +- { +- monitor->volumes = g_list_prepend (monitor->volumes, volume); +- *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume)); +- } +- } ++ volume = value; ++ gvfs_udisks2_volume_removed (volume); ++ *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume)); ++ remove_volume (monitor, volume); + } + +- g_list_free (added); +- g_list_free (removed); +- g_list_free (new_block_volumes); +- g_list_free (cur_block_volumes); ++ g_hash_table_unref (cur_block_volumes); + + g_list_free_full (objects, g_object_unref); + } +@@ -1590,182 +1639,189 @@ mount_point_has_device (GVfsUDisks2VolumeMonitor *monitor, + return ret; + } + ++static void ++free_nonnull_unix_mount_point (gpointer ptr) ++{ ++ GUnixMountPoint *mount_point = ptr; ++ if (mount_point != NULL) ++ g_unix_mount_point_free (mount_point); ++} ++ + static void + update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + GList **added_volumes, + GList **removed_volumes, + gboolean coldplug) + { +- GList *cur_mount_points; +- GList *new_mount_points; +- GList *added; +- GList *removed; +- GList *l, *ll; ++ GHashTable *cur_mount_points; /* GUnixMountPoint * ~> GVfsUDisks2Volume * */ ++ GHashTableIter iter; ++ gpointer key = NULL, value = NULL; ++ GList *new_mount_points, *l; + GVfsUDisks2Volume *volume; + +- cur_mount_points = NULL; +- for (l = monitor->fstab_volumes; l != NULL; l = l->next) ++ cur_mount_points = g_hash_table_new_full (gvfs_mount_point_hash, gvfs_mount_point_equal, NULL, g_object_unref); ++ ++ g_hash_table_iter_init (&iter, monitor->fstab_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- GUnixMountPoint *mount_point = gvfs_udisks2_volume_get_mount_point (GVFS_UDISKS2_VOLUME (l->data)); ++ GUnixMountPoint *mount_point; ++ volume = GVFS_UDISKS2_VOLUME (key); ++ mount_point = gvfs_udisks2_volume_get_mount_point (volume); + if (mount_point != NULL) +- cur_mount_points = g_list_prepend (cur_mount_points, mount_point); ++ g_hash_table_insert (cur_mount_points, mount_point, g_object_ref (volume)); + } + + new_mount_points = g_unix_mount_points_get (NULL); + /* filter the mount points that we don't want to include */ +- for (l = new_mount_points; l != NULL; l = ll) ++ for (l = new_mount_points; l != NULL; l = g_list_next (l)) + { + GUnixMountPoint *mount_point = l->data; + +- ll = l->next; +- +- if (!should_include_mount_point (monitor, mount_point) || +- have_udisks_volume_for_mount_point (monitor, mount_point) || +- !mount_point_has_device (monitor, mount_point)) ++ /* use the mount points that we want to include */ ++ if (should_include_mount_point (monitor, mount_point) && ++ !have_udisks_volume_for_mount_point (monitor, mount_point) && ++ mount_point_has_device (monitor, mount_point)) + { +- new_mount_points = g_list_delete_link (new_mount_points, l); +- g_unix_mount_point_free (mount_point); ++ /* not in currently known volumes => add it */ ++ if (!g_hash_table_remove (cur_mount_points, mount_point)) ++ { ++ volume = gvfs_udisks2_volume_new (monitor, ++ NULL, /* block */ ++ mount_point, ++ NULL, /* drive */ ++ NULL, /* activation_root */ ++ coldplug); ++ if (volume != NULL) ++ { ++ GVfsUDisks2Mount *mount; ++ ++ /* Could be there's already a mount for this volume - for example, the ++ * user could just have added it to the /etc/fstab file ++ */ ++ mount = find_lonely_mount_for_mount_point (monitor, mount_point); ++ if (mount != NULL) ++ gvfs_udisks2_mount_set_volume (mount, volume); ++ ++ g_hash_table_add (monitor->fstab_volumes, g_object_ref (volume)); ++ *added_volumes = g_list_prepend (*added_volumes, g_steal_pointer (&volume)); ++ l->data = NULL; ++ } ++ } + } + } + +- cur_mount_points = g_list_sort (cur_mount_points, (GCompareFunc) g_unix_mount_point_compare); +- new_mount_points = g_list_sort (new_mount_points, (GCompareFunc) g_unix_mount_point_compare); +- diff_sorted_lists (cur_mount_points, +- new_mount_points, (GCompareFunc) g_unix_mount_point_compare, +- &added, &removed, NULL); +- +- for (l = removed; l != NULL; l = l->next) ++ /* which left are removed */ ++ g_hash_table_iter_init (&iter, cur_mount_points); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { +- GUnixMountPoint *mount_point = l->data; +- volume = find_fstab_volume_for_mount_point (monitor, mount_point); +- if (volume != NULL) +- { +- gvfs_udisks2_volume_removed (volume); +- monitor->fstab_volumes = g_list_remove (monitor->fstab_volumes, volume); +- *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume)); +- g_object_unref (volume); +- } ++ volume = value; ++ gvfs_udisks2_volume_removed (volume); ++ *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume)); ++ g_hash_table_remove (monitor->fstab_volumes, volume); + } + +- for (l = added; l != NULL; l = l->next) +- { +- GUnixMountPoint *mount_point = l->data; ++ g_list_free_full (new_mount_points, free_nonnull_unix_mount_point); + +- volume = find_fstab_volume_for_mount_point (monitor, mount_point); +- if (volume != NULL) +- continue; +- +- volume = gvfs_udisks2_volume_new (monitor, +- NULL, /* block */ +- mount_point, +- NULL, /* drive */ +- NULL, /* activation_root */ +- coldplug); +- if (volume != NULL) +- { +- GVfsUDisks2Mount *mount; +- +- monitor->fstab_volumes = g_list_prepend (monitor->fstab_volumes, volume); +- *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume)); +- /* since @volume takes ownership of @mount_point, don't free it below */ +- new_mount_points = g_list_remove (new_mount_points, mount_point); +- +- /* Could be there's already a mount for this volume - for example, the +- * user could just have added it to the /etc/fstab file +- */ +- mount = find_lonely_mount_for_mount_point (monitor, mount_point); +- if (mount != NULL) +- gvfs_udisks2_mount_set_volume (mount, volume); +- } +- } ++ g_hash_table_unref (cur_mount_points); ++} + +- g_list_free_full (new_mount_points, (GDestroyNotify) g_unix_mount_point_free); ++/* ---------------------------------------------------------------------------------------------------- */ + +- g_list_free (cur_mount_points); ++static guint ++gvfs_mount_entry_hash (gconstpointer ptr) ++{ ++ GUnixMountEntry *mount_entry = (GUnixMountEntry *) ptr; ++ return g_str_hash (g_unix_mount_get_mount_path (mount_entry)) ^ ++ g_str_hash (g_unix_mount_get_device_path (mount_entry)); + } + +-/* ---------------------------------------------------------------------------------------------------- */ ++static gboolean ++gvfs_mount_entry_equal (gconstpointer ptr1, ++ gconstpointer ptr2) ++{ ++ GUnixMountEntry *mount_entry1 = (GUnixMountEntry *) ptr1; ++ GUnixMountEntry *mount_entry2 = (GUnixMountEntry *) ptr2; ++ return g_unix_mount_compare (mount_entry1, mount_entry2) == 0; ++} + + static void + update_mounts (GVfsUDisks2VolumeMonitor *monitor, ++ GHashTable *mount_entries, /* gchar *path ~> GUnixMountEntry * */ ++ GHashTable *mount_points_by_path, /* gchar *path ~> GUnixMountPoint * */ + GList **added_mounts, + GList **removed_mounts, + gboolean coldplug) + { +- GList *cur_mounts; +- GList *new_mounts; +- GList *removed, *added, *unchanged; +- GList *l, *ll; ++ GHashTable *cur_mounts; /* GUnixMountEntry * ~> GVfsUDisks2Mount * */ ++ GHashTableIter iter; ++ gpointer value = NULL; ++ GList *l, *unchanged = NULL; /* GVfsUDisks2Mount * */ + GVfsUDisks2Mount *mount; + GVfsUDisks2Volume *volume; + +- cur_mounts = NULL; +- for (l = monitor->mounts; l != NULL; l = l->next) ++ cur_mounts = g_hash_table_new_full (gvfs_mount_entry_hash, gvfs_mount_entry_equal, NULL, g_object_unref); ++ ++ g_hash_table_iter_init (&iter, monitor->mounts); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { + GUnixMountEntry *mount_entry; + +- mount = GVFS_UDISKS2_MOUNT (l->data); ++ mount = GVFS_UDISKS2_MOUNT (value); + mount_entry = gvfs_udisks2_mount_get_mount_entry (mount); + if (mount_entry != NULL) +- cur_mounts = g_list_prepend (cur_mounts, mount_entry); ++ g_hash_table_insert (cur_mounts, mount_entry, g_object_ref (mount)); + } + +- new_mounts = g_unix_mounts_get (NULL); +- /* remove mounts we want to ignore - we do it here so we get to reevaluate ++ /* skip mounts we want to ignore - we do it here so we get to reevaluate + * on the next update whether they should still be ignored + */ +- for (l = new_mounts; l != NULL; l = ll) ++ g_hash_table_iter_init (&iter, mount_entries); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { +- GUnixMountEntry *mount_entry = l->data; +- ll = l->next; +- if (!should_include_mount (monitor, mount_entry)) ++ GUnixMountEntry *mount_entry = value; ++ if (should_include_mount (monitor, mount_entry, mount_points_by_path)) + { +- g_unix_mount_free (mount_entry); +- new_mounts = g_list_delete_link (new_mounts, l); +- } +- } +- +- cur_mounts = g_list_sort (cur_mounts, (GCompareFunc) g_unix_mount_compare); +- new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare); +- diff_sorted_lists (cur_mounts, +- new_mounts, (GCompareFunc) g_unix_mount_compare, +- &added, &removed, &unchanged); ++ mount = g_hash_table_lookup (cur_mounts, mount_entry); ++ if (mount != NULL) ++ { ++ unchanged = g_list_prepend (unchanged, g_object_ref (mount)); ++ g_hash_table_remove (cur_mounts, mount_entry); ++ } ++ else ++ { ++ const gchar *root = NULL; ++ const gchar *device_path; + +- for (l = removed; l != NULL; l = l->next) +- { +- GUnixMountEntry *mount_entry = l->data; +- mount = find_mount_by_mount_path (monitor, g_unix_mount_get_mount_path (mount_entry)); +- if (mount != NULL) +- { +- gvfs_udisks2_mount_unmounted (mount); +- monitor->mounts = g_list_remove (monitor->mounts, mount); +- *removed_mounts = g_list_prepend (*removed_mounts, g_object_ref (mount)); +- g_object_unref (mount); ++ /* since @mount takes ownership of @mount_entry, create a copy to not free it from the mount_entries */ ++ mount_entry = g_unix_mount_copy (mount_entry); ++ device_path = g_unix_mount_get_device_path (mount_entry); ++#ifdef HAVE_G_UNIX_MOUNT_GET_ROOT_PATH ++ root = g_unix_mount_get_root_path (mount_entry); ++#endif ++ volume = NULL; ++ if (root == NULL || g_strcmp0 (root, "/") == 0) ++ volume = find_volume_for_device (monitor, device_path); ++ if (volume == NULL) ++ volume = find_fstab_volume_for_mount_entry (monitor, mount_entry); ++ mount = gvfs_udisks2_mount_new (monitor, mount_entry, volume); /* adopts mount_entry */ ++ if (mount != NULL) ++ { ++ g_hash_table_insert (monitor->mounts, g_strdup (gvfs_udisks2_mount_get_mount_path (mount)), g_object_ref (mount)); ++ *added_mounts = g_list_prepend (*added_mounts, g_steal_pointer (&mount)); ++ } ++ } + } + } + +- for (l = added; l != NULL; l = l->next) ++ g_hash_table_iter_init (&iter, cur_mounts); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { +- GUnixMountEntry *mount_entry = l->data; +- const gchar *root = NULL; +- +-#ifdef HAVE_G_UNIX_MOUNT_GET_ROOT_PATH +- root = g_unix_mount_get_root_path (mount_entry); +-#endif ++ mount = value; ++ gvfs_udisks2_mount_unmounted (mount); ++ *removed_mounts = g_list_prepend (*removed_mounts, g_object_ref (mount)); + +- volume = NULL; +- if (root == NULL || g_strcmp0 (root, "/") == 0) +- volume = find_volume_for_device (monitor, g_unix_mount_get_device_path (mount_entry)); +- if (volume == NULL) +- volume = find_fstab_volume_for_mount_entry (monitor, mount_entry); +- mount = gvfs_udisks2_mount_new (monitor, mount_entry, volume); /* adopts mount_entry */ +- if (mount != NULL) +- { +- monitor->mounts = g_list_prepend (monitor->mounts, mount); +- *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount)); +- /* since @mount takes ownership of @mount_entry, don't free it below */ +- new_mounts = g_list_remove (new_mounts, mount_entry); +- } ++ if (g_hash_table_lookup (monitor->mounts, gvfs_udisks2_mount_get_mount_path (mount)) == mount) ++ g_hash_table_remove (monitor->mounts, gvfs_udisks2_mount_get_mount_path (mount)); + } + + /* Handle the case where the volume containing the mount appears *after* +@@ -1778,24 +1834,23 @@ update_mounts (GVfsUDisks2VolumeMonitor *monitor, + */ + for (l = unchanged; l != NULL; l = l->next) + { +- GUnixMountEntry *mount_entry = l->data; +- mount = find_mount_by_mount_path (monitor, g_unix_mount_get_mount_path (mount_entry)); +- if (mount == NULL) +- { +- g_warning ("No mount object for path %s", g_unix_mount_get_mount_path (mount_entry)); +- continue; +- } ++ mount = l->data; ++ + if (gvfs_udisks2_mount_get_volume (mount) == NULL) + { ++ GUnixMountEntry *mount_entry = gvfs_udisks2_mount_get_mount_entry (mount); ++ const gchar *device_path; + const gchar *root = NULL; + ++ device_path = g_unix_mount_get_device_path (mount_entry); ++ + #ifdef HAVE_G_UNIX_MOUNT_GET_ROOT_PATH + root = g_unix_mount_get_root_path (mount_entry); + #endif + + volume = NULL; + if (root == NULL || g_strcmp0 (root, "/") == 0) +- volume = find_volume_for_device (monitor, g_unix_mount_get_device_path (mount_entry)); ++ volume = find_volume_for_device (monitor, device_path); + if (volume == NULL) + volume = find_fstab_volume_for_mount_entry (monitor, mount_entry); + if (volume != NULL) +@@ -1803,13 +1858,9 @@ update_mounts (GVfsUDisks2VolumeMonitor *monitor, + } + } + +- g_list_free (added); +- g_list_free (removed); +- g_list_free (unchanged); ++ g_list_free_full (unchanged, g_object_unref); + +- g_list_free_full (new_mounts, (GDestroyNotify) g_unix_mount_free); +- +- g_list_free (cur_mounts); ++ g_hash_table_unref (cur_mounts); + } + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -1822,11 +1873,10 @@ update_discs (GVfsUDisks2VolumeMonitor *monitor, + GList **removed_mounts, + gboolean coldplug) + { +- GList *objects; +- GList *cur_disc_block_volumes; +- GList *new_disc_block_volumes; +- GList *removed, *added; +- GList *l; ++ GList *objects, *l; ++ GHashTable *cur_disc_block_volumes; /* UDisksBlock * ~> GVfsUDisks2Volume * */ ++ GHashTableIter iter; ++ gpointer key = NULL, value = NULL; + GVfsUDisks2Volume *volume; + GVfsUDisks2Mount *mount; + +@@ -1839,14 +1889,15 @@ update_discs (GVfsUDisks2VolumeMonitor *monitor, + + objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (monitor->client)); + +- cur_disc_block_volumes = NULL; +- for (l = monitor->disc_volumes; l != NULL; l = l->next) ++ cur_disc_block_volumes = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref); ++ ++ g_hash_table_iter_init (&iter, monitor->disc_volumes); ++ while (g_hash_table_iter_next (&iter, &key, NULL)) + { +- cur_disc_block_volumes = g_list_prepend (cur_disc_block_volumes, +- gvfs_udisks2_volume_get_block (GVFS_UDISKS2_VOLUME (l->data))); ++ volume = key; ++ g_hash_table_insert (cur_disc_block_volumes, gvfs_udisks2_volume_get_block (volume), g_object_ref (volume)); + } + +- new_disc_block_volumes = NULL; + for (l = objects; l != NULL; l = l->next) + { + UDisksDrive *udisks_drive = udisks_object_peek_drive (UDISKS_OBJECT (l->data)); +@@ -1865,19 +1916,68 @@ update_discs (GVfsUDisks2VolumeMonitor *monitor, + + block = udisks_client_get_block_for_drive (monitor->client, udisks_drive, FALSE); + if (block != NULL) +- new_disc_block_volumes = g_list_prepend (new_disc_block_volumes, block); +- } ++ { ++ /* not in currently known volumes => add it */ ++ if (!g_hash_table_remove (cur_disc_block_volumes, block)) ++ { ++ UDisksDrive *udisks_drive; + +- cur_disc_block_volumes = g_list_sort (cur_disc_block_volumes, (GCompareFunc) block_compare); +- new_disc_block_volumes = g_list_sort (new_disc_block_volumes, (GCompareFunc) block_compare); +- diff_sorted_lists (cur_disc_block_volumes, new_disc_block_volumes, (GCompareFunc) block_compare, +- &added, &removed, NULL); ++ udisks_drive = udisks_client_get_drive_for_block (monitor->client, block); ++ if (udisks_drive != NULL) ++ { ++ gchar *uri = NULL; ++ GFile *activation_root; + +- for (l = removed; l != NULL; l = l->next) +- { +- UDisksBlock *block = UDISKS_BLOCK (l->data); ++ if (udisks_drive_get_optical_blank (udisks_drive)) ++ { ++ uri = g_strdup ("burn://"); ++ } ++ else ++ { ++ gchar *basename = g_path_get_basename (udisks_block_get_device (block)); ++ uri = g_strdup_printf ("cdda://%s", basename); ++ g_free (basename); ++ } ++ ++ activation_root = g_file_new_for_uri (uri); ++ volume = gvfs_udisks2_volume_new (monitor, ++ block, ++ NULL, /* mount_point */ ++ find_drive_for_udisks_drive (monitor, udisks_drive), ++ activation_root, ++ coldplug); ++ if (volume != NULL) ++ { ++ if (udisks_drive_get_optical_blank (udisks_drive)) ++ { ++ mount = gvfs_udisks2_mount_new (monitor, ++ NULL, /* GUnixMountEntry */ ++ volume); ++ if (mount != NULL) ++ { ++ g_hash_table_add (monitor->disc_mounts, g_object_ref (mount)); ++ *added_mounts = g_list_prepend (*added_mounts, g_steal_pointer (&mount)); ++ } ++ } ++ ++ add_disc_volume (monitor, volume); ++ *added_volumes = g_list_prepend (*added_volumes, g_steal_pointer (&volume)); ++ } + +- volume = find_disc_volume_for_block (monitor, block); ++ g_object_unref (activation_root); ++ g_free (uri); ++ g_object_unref (udisks_drive); ++ } ++ } ++ g_object_unref (block); ++ } ++ } ++ ++ /* which left are removed */ ++ g_hash_table_iter_init (&iter, cur_disc_block_volumes); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) ++ { ++ volume = value; + + mount = NULL; + if (volume != NULL) +@@ -1886,80 +1986,18 @@ update_discs (GVfsUDisks2VolumeMonitor *monitor, + if (mount != NULL) + { + gvfs_udisks2_mount_unmounted (mount); +- monitor->disc_mounts = g_list_remove (monitor->disc_mounts, mount); +- *removed_mounts = g_list_prepend (*removed_mounts, mount); ++ *removed_mounts = g_list_prepend (*removed_mounts, g_object_ref (mount)); ++ g_hash_table_remove (monitor->disc_mounts, mount); + } + if (volume != NULL) + { + gvfs_udisks2_volume_removed (volume); +- monitor->disc_volumes = g_list_remove (monitor->disc_volumes, volume); +- *removed_volumes = g_list_prepend (*removed_volumes, volume); +- } +- } +- +- for (l = added; l != NULL; l = l->next) +- { +- UDisksBlock *block = UDISKS_BLOCK (l->data); +- +- volume = find_disc_volume_for_block (monitor, block); +- if (volume == NULL) +- { +- UDisksDrive *udisks_drive; +- +- udisks_drive = udisks_client_get_drive_for_block (monitor->client, block); +- if (udisks_drive != NULL) +- { +- gchar *uri; +- GFile *activation_root; +- +- if (udisks_drive_get_optical_blank (udisks_drive)) +- { +- uri = g_strdup ("burn://"); +- } +- else +- { +- gchar *basename = g_path_get_basename (udisks_block_get_device (block)); +- uri = g_strdup_printf ("cdda://%s", basename); +- g_free (basename); +- } +- activation_root = g_file_new_for_uri (uri); +- volume = gvfs_udisks2_volume_new (monitor, +- block, +- NULL, /* mount_point */ +- find_drive_for_udisks_drive (monitor, udisks_drive), +- activation_root, +- coldplug); +- if (volume != NULL) +- { +- monitor->disc_volumes = g_list_prepend (monitor->disc_volumes, volume); +- *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume)); +- +- if (udisks_drive_get_optical_blank (udisks_drive)) +- { +- mount = gvfs_udisks2_mount_new (monitor, +- NULL, /* GUnixMountEntry */ +- volume); +- if (mount != NULL) +- { +- monitor->disc_mounts = g_list_prepend (monitor->disc_mounts, mount); +- *added_mounts = g_list_prepend (*added_mounts, g_object_ref (mount)); +- } +- } +- } +- +- g_object_unref (activation_root); +- g_free (uri); +- g_object_unref (udisks_drive); +- } ++ *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume)); ++ remove_disc_volume (monitor, volume); + } +- g_object_unref (block); + } + +- g_list_free (added); +- g_list_free (removed); +- +- g_list_free (new_disc_block_volumes); +- g_list_free (cur_disc_block_volumes); ++ g_hash_table_unref (cur_disc_block_volumes); + + g_list_free_full (objects, g_object_unref); + } diff --git a/udisks2-monitor-performance-290.patch b/udisks2-monitor-performance-290.patch new file mode 100644 index 0000000..f111a91 --- /dev/null +++ b/udisks2-monitor-performance-290.patch @@ -0,0 +1,90 @@ +Adapted https://gitlab.gnome.org/GNOME/gvfs/-/merge_requests/290 + +diff --git a/monitor/udisks2/gvfsudisks2volumemonitor.c b/monitor/udisks2/gvfsudisks2volumemonitor.c +index 51068f9..48120d9 100644 +--- a/monitor/udisks2/gvfsudisks2volumemonitor.c ++++ b/monitor/udisks2/gvfsudisks2volumemonitor.c +@@ -92,6 +92,7 @@ static void update_volumes (GVfsUDisks2VolumeMonitor *monitor, + GList **removed_volumes, + gboolean coldplug); + static void update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, ++ GHashTable *mount_points_by_path, /* gchar *path ~> GUnixMountPoint * */ + GList **added_volumes, + GList **removed_volumes, + gboolean coldplug); +@@ -749,7 +750,7 @@ update_all (GVfsUDisks2VolumeMonitor *monitor, + + update_drives (monitor, &added_drives, &removed_drives, coldplug); + update_volumes (monitor, mount_entries, mount_points_by_path, &added_volumes, &removed_volumes, coldplug); +- update_fstab_volumes (monitor, &added_volumes, &removed_volumes, coldplug); ++ update_fstab_volumes (monitor, mount_points_by_path, &added_volumes, &removed_volumes, coldplug); + update_mounts (monitor, mount_entries, mount_points_by_path, &added_mounts, &removed_mounts, coldplug); + update_discs (monitor, + &added_volumes, &removed_volumes, +@@ -1637,16 +1638,9 @@ mount_point_has_device (GVfsUDisks2VolumeMonitor *monitor, + return ret; + } + +-static void +-free_nonnull_unix_mount_point (gpointer ptr) +-{ +- GUnixMountPoint *mount_point = ptr; +- if (mount_point != NULL) +- g_unix_mount_point_free (mount_point); +-} +- + static void + update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, ++ GHashTable *mount_points_by_path, /* gchar *path ~> GUnixMountPoint * */ + GList **added_volumes, + GList **removed_volumes, + gboolean coldplug) +@@ -1654,7 +1648,6 @@ update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + GHashTable *cur_mount_points; /* GUnixMountPoint * ~> GVfsUDisks2Volume * */ + GHashTableIter iter; + gpointer key = NULL, value = NULL; +- GList *new_mount_points, *l; + GVfsUDisks2Volume *volume; + + cur_mount_points = g_hash_table_new_full (gvfs_mount_point_hash, gvfs_mount_point_equal, NULL, g_object_unref); +@@ -1669,11 +1662,10 @@ update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + g_hash_table_insert (cur_mount_points, mount_point, g_object_ref (volume)); + } + +- new_mount_points = g_unix_mount_points_get (NULL); +- /* filter the mount points that we don't want to include */ +- for (l = new_mount_points; l != NULL; l = g_list_next (l)) ++ g_hash_table_iter_init (&iter, mount_points_by_path); ++ while (g_hash_table_iter_next (&iter, NULL, &value)) + { +- GUnixMountPoint *mount_point = l->data; ++ GUnixMountPoint *mount_point = value; + + /* use the mount points that we want to include */ + if (should_include_mount_point (monitor, mount_point) && +@@ -1685,7 +1677,7 @@ update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + { + volume = gvfs_udisks2_volume_new (monitor, + NULL, /* block */ +- mount_point, ++ g_unix_mount_point_copy (mount_point), /* adopts the mount_point */ + NULL, /* drive */ + NULL, /* activation_root */ + coldplug); +@@ -1702,7 +1694,6 @@ update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + + g_hash_table_add (monitor->fstab_volumes, g_object_ref (volume)); + *added_volumes = g_list_prepend (*added_volumes, g_steal_pointer (&volume)); +- l->data = NULL; + } + } + } +@@ -1718,8 +1709,6 @@ update_fstab_volumes (GVfsUDisks2VolumeMonitor *monitor, + g_hash_table_remove (monitor->fstab_volumes, volume); + } + +- g_list_free_full (new_mount_points, free_nonnull_unix_mount_point); +- + g_hash_table_unref (cur_mount_points); + } +