Blob Blame History Raw
From dca731ff0daf904911dd6815fb9a1b181329c887 Mon Sep 17 00:00:00 2001
From: Milan Crha <mcrha@redhat.com>
Date: Tue, 5 Oct 2021 11:00:20 +0200
Subject: [PATCH 1/4] gs-repo-row: Use GS_APP_QUIRK_COMPULSORY to recognize
 required repositories

The GS_APP_QUIRK_PROVENANCE quirk does not mean it's also required repository,
thus use the GS_APP_QUIRK_COMPULSORY for repos, which cannot be disabled.
The GS_APP_QUIRK_PROVENANCE is used only for repositories, which cannot be removed.
---
 src/gs-repo-row.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/gs-repo-row.c b/src/gs-repo-row.c
index 87926092f..bbf67c194 100644
--- a/src/gs-repo-row.c
+++ b/src/gs-repo-row.c
@@ -48,7 +48,8 @@ refresh_ui (GsRepoRow *row)
 	gboolean active = FALSE;
 	gboolean state_sensitive = FALSE;
 	gboolean busy = priv->busy_counter> 0;
-	gboolean is_system_repo;
+	gboolean is_provenance;
+	gboolean is_compulsory;
 
 	if (priv->repo == NULL) {
 		gtk_widget_set_sensitive (priv->disable_switch, FALSE);
@@ -87,11 +88,12 @@ refresh_ui (GsRepoRow *row)
 		break;
 	}
 
-	is_system_repo = gs_app_has_quirk (priv->repo, GS_APP_QUIRK_PROVENANCE);
+	is_provenance = gs_app_has_quirk (priv->repo, GS_APP_QUIRK_PROVENANCE);
+	is_compulsory = gs_app_has_quirk (priv->repo, GS_APP_QUIRK_COMPULSORY);
 
 	/* Disable for the system repos, if installed */
-	gtk_widget_set_sensitive (priv->disable_switch, priv->supports_enable_disable && (state_sensitive || !is_system_repo || priv->always_allow_enable_disable));
-	gtk_widget_set_visible (priv->remove_button, priv->supports_remove && !is_system_repo);
+	gtk_widget_set_sensitive (priv->disable_switch, priv->supports_enable_disable && (state_sensitive || !is_compulsory || priv->always_allow_enable_disable));
+	gtk_widget_set_visible (priv->remove_button, priv->supports_remove && !is_provenance && !is_compulsory);
 
 	/* Set only the 'state' to visually indicate the state is not saved yet */
 	if (busy)
-- 
GitLab


From 026218b9d3211de243dfc49eca8b8d46633882b0 Mon Sep 17 00:00:00 2001
From: Milan Crha <mcrha@redhat.com>
Date: Tue, 5 Oct 2021 11:03:31 +0200
Subject: [PATCH 2/4] gs-plugin-provenance: Improve search speed in list of
 repositories

Use a GHashTable for bare repository names and a GPtrArray for those
with wildcards. This helps with speed, due to not traversing all
the repository names with the fnmatch() call.
---
 plugins/core/gs-plugin-provenance.c | 55 ++++++++++++++++++++---------
 1 file changed, 38 insertions(+), 17 deletions(-)

diff --git a/plugins/core/gs-plugin-provenance.c b/plugins/core/gs-plugin-provenance.c
index 97ff76798..a72c25a27 100644
--- a/plugins/core/gs-plugin-provenance.c
+++ b/plugins/core/gs-plugin-provenance.c
@@ -19,7 +19,8 @@
 
 struct GsPluginData {
 	GSettings		*settings;
-	gchar			**sources;
+	GHashTable		*repos; /* gchar *name ~> NULL */
+	GPtrArray		*wildcards; /* non-NULL, when have names with wildcards */
 };
 
 static gchar **
@@ -42,8 +43,24 @@ gs_plugin_provenance_settings_changed_cb (GSettings *settings,
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
 	if (g_strcmp0 (key, "official-repos") == 0) {
-		g_strfreev (priv->sources);
-		priv->sources = gs_plugin_provenance_get_sources (plugin);
+		/* The keys are stolen by the hash table, thus free only the array */
+		g_autofree gchar **repos = NULL;
+		g_hash_table_remove_all (priv->repos);
+		g_clear_pointer (&priv->wildcards, g_ptr_array_unref);
+		repos = gs_plugin_provenance_get_sources (plugin);
+		for (guint ii = 0; repos && repos[ii]; ii++) {
+			if (strchr (repos[ii], '*') ||
+			    strchr (repos[ii], '?') ||
+			    strchr (repos[ii], '[')) {
+				if (priv->wildcards == NULL)
+					priv->wildcards = g_ptr_array_new_with_free_func (g_free);
+				g_ptr_array_add (priv->wildcards, g_steal_pointer (&(repos[ii])));
+			} else {
+				g_hash_table_insert (priv->repos, g_steal_pointer (&(repos[ii])), NULL);
+			}
+		}
+		if (priv->wildcards != NULL)
+			g_ptr_array_add (priv->wildcards, NULL);
 	}
 }
 
@@ -52,9 +69,10 @@ gs_plugin_initialize (GsPlugin *plugin)
 {
 	GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData));
 	priv->settings = g_settings_new ("org.gnome.software");
+	priv->repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 	g_signal_connect (priv->settings, "changed",
 			  G_CALLBACK (gs_plugin_provenance_settings_changed_cb), plugin);
-	priv->sources = gs_plugin_provenance_get_sources (plugin);
+	gs_plugin_provenance_settings_changed_cb (priv->settings, "official-repos", plugin);
 
 	/* after the package source is set */
 	gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "dummy");
@@ -66,7 +84,8 @@ void
 gs_plugin_destroy (GsPlugin *plugin)
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
-	g_strfreev (priv->sources);
+	g_hash_table_unref (priv->repos);
+	g_clear_pointer (&priv->wildcards, g_ptr_array_unref);
 	g_object_unref (priv->settings);
 }
 
@@ -74,12 +93,12 @@ static gboolean
 refine_app (GsPlugin             *plugin,
 	    GsApp                *app,
 	    GsPluginRefineFlags   flags,
+	    GHashTable		 *repos,
+	    GPtrArray		 *wildcards,
 	    GCancellable         *cancellable,
 	    GError              **error)
 {
-	GsPluginData *priv = gs_plugin_get_data (plugin);
 	const gchar *origin;
-	gchar **sources;
 
 	/* not required */
 	if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0)
@@ -87,14 +106,10 @@ refine_app (GsPlugin             *plugin,
 	if (gs_app_has_quirk (app, GS_APP_QUIRK_PROVENANCE))
 		return TRUE;
 
-	/* nothing to search */
-	sources = priv->sources;
-	if (sources == NULL || sources[0] == NULL)
-		return TRUE;
-
 	/* simple case */
 	origin = gs_app_get_origin (app);
-	if (origin != NULL && gs_utils_strv_fnmatch (sources, origin)) {
+	if (origin != NULL && (g_hash_table_contains (repos, origin) ||
+	    (wildcards != NULL && gs_utils_strv_fnmatch ((gchar **) wildcards->pdata, origin)))) {
 		gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
 		return TRUE;
 	}
@@ -103,7 +118,8 @@ refine_app (GsPlugin             *plugin,
 	 * provenance quirk to the system-configured repositories (but not
 	 * user-configured ones). */
 	if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY &&
-	    gs_utils_strv_fnmatch (sources, gs_app_get_id (app))) {
+	    (g_hash_table_contains (repos, gs_app_get_id (app)) ||
+	    (wildcards != NULL && gs_utils_strv_fnmatch ((gchar **) wildcards->pdata, gs_app_get_id (app))))) {
 		if (gs_app_get_scope (app) != AS_COMPONENT_SCOPE_USER)
 			gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
 		return TRUE;
@@ -118,7 +134,8 @@ refine_app (GsPlugin             *plugin,
 		return TRUE;
 	if (g_str_has_prefix (origin + 1, "installed:"))
 		origin += 10;
-	if (gs_utils_strv_fnmatch (sources, origin + 1)) {
+	if (g_hash_table_contains (repos, origin + 1) ||
+	    (wildcards != NULL && gs_utils_strv_fnmatch ((gchar **) wildcards->pdata, origin + 1))) {
 		gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
 		return TRUE;
 	}
@@ -133,17 +150,21 @@ gs_plugin_refine (GsPlugin             *plugin,
 		  GError              **error)
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
+	g_autoptr(GHashTable) repos = NULL;
+	g_autoptr(GPtrArray) wildcards = NULL;
 
 	/* nothing to do here */
 	if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0)
 		return TRUE;
+	repos = g_hash_table_ref (priv->repos);
+	wildcards = priv->wildcards != NULL ? g_ptr_array_ref (priv->wildcards) : NULL;
 	/* nothing to search */
-	if (priv->sources == NULL || priv->sources[0] == NULL)
+	if (g_hash_table_size (repos) == 0)
 		return TRUE;
 
 	for (guint i = 0; i < gs_app_list_length (list); i++) {
 		GsApp *app = gs_app_list_index (list, i);
-		if (!refine_app (plugin, app, flags, cancellable, error))
+		if (!refine_app (plugin, app, flags, repos, wildcards, cancellable, error))
 			return FALSE;
 	}
 
-- 
GitLab


From b5e3356aff5fcd257248f9bb697e272c879249ae Mon Sep 17 00:00:00 2001
From: Milan Crha <mcrha@redhat.com>
Date: Tue, 5 Oct 2021 13:03:44 +0200
Subject: [PATCH 3/4] settings: Add 'required-repos' key

To be used to list repositories, which cannot be removed or disabled.
It's a complementary option for the 'official-repos' key.
---
 data/org.gnome.software.gschema.xml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/data/org.gnome.software.gschema.xml b/data/org.gnome.software.gschema.xml
index db1c27ce4..0e5706b7c 100644
--- a/data/org.gnome.software.gschema.xml
+++ b/data/org.gnome.software.gschema.xml
@@ -94,6 +94,10 @@
       <default>[]</default>
       <summary>A list of official repositories that should not be considered 3rd party</summary>
     </key>
+    <key name="required-repos" type="as">
+      <default>[]</default>
+      <summary>A list of required repositories that cannot be disabled or removed</summary>
+    </key>
     <key name="free-repos" type="as">
       <default>[]</default>
       <summary>A list of official repositories that should be considered free software</summary>
-- 
GitLab


From d6b8b206a596bb520a0b77066898b44a5ef18920 Mon Sep 17 00:00:00 2001
From: Milan Crha <mcrha@redhat.com>
Date: Tue, 5 Oct 2021 14:16:56 +0200
Subject: [PATCH 4/4] gs-plugin-provenance: Handle also 'required-repos' key

Let it handle also 'required-repos' settings key, beside the 'official-repos'
key, which are close enough to share the same code and memory. With this
done the repositories can be marked as compulsory, independently from the official
repositories.

Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1479
---
 plugins/core/gs-plugin-provenance.c | 142 +++++++++++++++++++++-------
 1 file changed, 108 insertions(+), 34 deletions(-)

diff --git a/plugins/core/gs-plugin-provenance.c b/plugins/core/gs-plugin-provenance.c
index a72c25a27..22f3c98e1 100644
--- a/plugins/core/gs-plugin-provenance.c
+++ b/plugins/core/gs-plugin-provenance.c
@@ -14,26 +14,61 @@
 /*
  * SECTION:
  * Sets the package provenance to TRUE if installed by an official
- * software source.
+ * software source. Also sets compulsory quirk when a required repository.
  */
 
 struct GsPluginData {
 	GSettings		*settings;
-	GHashTable		*repos; /* gchar *name ~> NULL */
-	GPtrArray		*wildcards; /* non-NULL, when have names with wildcards */
+	GHashTable		*repos; /* gchar *name ~> guint flags */
+	GPtrArray		*provenance_wildcards; /* non-NULL, when have names with wildcards */
+	GPtrArray		*compulsory_wildcards; /* non-NULL, when have names with wildcards */
 };
 
+static GHashTable *
+gs_plugin_provenance_remove_by_flag (GHashTable *old_repos,
+				     GsAppQuirk quirk)
+{
+	GHashTable *new_repos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	GHashTableIter iter;
+	gpointer key, value;
+	g_hash_table_iter_init (&iter, old_repos);
+	while (g_hash_table_iter_next (&iter, &key, &value)) {
+		guint flags = GPOINTER_TO_UINT (value);
+		flags = flags & (~quirk);
+		if (flags != 0)
+			g_hash_table_insert (new_repos, g_strdup (key), GUINT_TO_POINTER (flags));
+	}
+	return new_repos;
+}
+
+static void
+gs_plugin_provenance_add_quirks (GsApp *app,
+				 guint quirks)
+{
+	GsAppQuirk array[] = {
+		GS_APP_QUIRK_PROVENANCE,
+		GS_APP_QUIRK_COMPULSORY
+	};
+	for (guint ii = 0; ii < G_N_ELEMENTS (array); ii++) {
+		if ((quirks & array[ii]) != 0)
+			gs_app_add_quirk (app, array[ii]);
+	}
+}
+
 static gchar **
-gs_plugin_provenance_get_sources (GsPlugin *plugin)
+gs_plugin_provenance_get_sources (GsPlugin *plugin,
+				  const gchar *key)
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
 	const gchar *tmp;
 	tmp = g_getenv ("GS_SELF_TEST_PROVENANCE_SOURCES");
 	if (tmp != NULL) {
+		if (g_strcmp0 (key, "required-repos") == 0)
+			return NULL;
 		g_debug ("using custom provenance sources of %s", tmp);
 		return g_strsplit (tmp, ",", -1);
 	}
-	return g_settings_get_strv (priv->settings, "official-repos");
+	return g_settings_get_strv (priv->settings, key);
 }
 
 static void
@@ -42,25 +77,43 @@ gs_plugin_provenance_settings_changed_cb (GSettings *settings,
 					  GsPlugin *plugin)
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
+	GsAppQuirk quirk = GS_APP_QUIRK_NONE;
+	GPtrArray **pwildcards = NULL;
+
 	if (g_strcmp0 (key, "official-repos") == 0) {
+		quirk = GS_APP_QUIRK_PROVENANCE;
+		pwildcards = &priv->provenance_wildcards;
+	} else if (g_strcmp0 (key, "required-repos") == 0) {
+		quirk = GS_APP_QUIRK_COMPULSORY;
+		pwildcards = &priv->compulsory_wildcards;
+	}
+
+	if (quirk != GS_APP_QUIRK_NONE) {
 		/* The keys are stolen by the hash table, thus free only the array */
 		g_autofree gchar **repos = NULL;
-		g_hash_table_remove_all (priv->repos);
-		g_clear_pointer (&priv->wildcards, g_ptr_array_unref);
-		repos = gs_plugin_provenance_get_sources (plugin);
+		g_autoptr(GHashTable) old_repos = priv->repos;
+		g_autoptr(GPtrArray) old_wildcards = *pwildcards;
+		GHashTable *new_repos = gs_plugin_provenance_remove_by_flag (old_repos, quirk);
+		GPtrArray *new_wildcards = NULL;
+		repos = gs_plugin_provenance_get_sources (plugin, key);
 		for (guint ii = 0; repos && repos[ii]; ii++) {
-			if (strchr (repos[ii], '*') ||
-			    strchr (repos[ii], '?') ||
-			    strchr (repos[ii], '[')) {
-				if (priv->wildcards == NULL)
-					priv->wildcards = g_ptr_array_new_with_free_func (g_free);
-				g_ptr_array_add (priv->wildcards, g_steal_pointer (&(repos[ii])));
+			gchar *repo = g_steal_pointer (&(repos[ii]));
+			if (strchr (repo, '*') ||
+			    strchr (repo, '?') ||
+			    strchr (repo, '[')) {
+				if (new_wildcards == NULL)
+					new_wildcards = g_ptr_array_new_with_free_func (g_free);
+				g_ptr_array_add (new_wildcards, repo);
 			} else {
-				g_hash_table_insert (priv->repos, g_steal_pointer (&(repos[ii])), NULL);
+				g_hash_table_insert (new_repos, repo,
+					GUINT_TO_POINTER (quirk |
+					GPOINTER_TO_UINT (g_hash_table_lookup (new_repos, repo))));
 			}
 		}
-		if (priv->wildcards != NULL)
-			g_ptr_array_add (priv->wildcards, NULL);
+		if (new_wildcards != NULL)
+			g_ptr_array_add (new_wildcards, NULL);
+		priv->repos = new_repos;
+		*pwildcards = new_wildcards;
 	}
 }
 
@@ -73,6 +126,7 @@ gs_plugin_initialize (GsPlugin *plugin)
 	g_signal_connect (priv->settings, "changed",
 			  G_CALLBACK (gs_plugin_provenance_settings_changed_cb), plugin);
 	gs_plugin_provenance_settings_changed_cb (priv->settings, "official-repos", plugin);
+	gs_plugin_provenance_settings_changed_cb (priv->settings, "required-repos", plugin);
 
 	/* after the package source is set */
 	gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "dummy");
@@ -85,20 +139,42 @@ gs_plugin_destroy (GsPlugin *plugin)
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
 	g_hash_table_unref (priv->repos);
-	g_clear_pointer (&priv->wildcards, g_ptr_array_unref);
+	g_clear_pointer (&priv->provenance_wildcards, g_ptr_array_unref);
+	g_clear_pointer (&priv->compulsory_wildcards, g_ptr_array_unref);
 	g_object_unref (priv->settings);
 }
 
+static gboolean
+gs_plugin_provenance_find_repo_flags (GHashTable *repos,
+				      GPtrArray *provenance_wildcards,
+				      GPtrArray *compulsory_wildcards,
+				      const gchar *repo,
+				      guint *out_flags)
+{
+	if (repo == NULL || *repo == '\0')
+		return FALSE;
+	*out_flags = GPOINTER_TO_UINT (g_hash_table_lookup (repos, repo));
+	if (provenance_wildcards != NULL &&
+	    gs_utils_strv_fnmatch ((gchar **) provenance_wildcards->pdata, repo))
+		*out_flags |= GS_APP_QUIRK_PROVENANCE;
+	if (compulsory_wildcards != NULL &&
+	    gs_utils_strv_fnmatch ((gchar **) compulsory_wildcards->pdata, repo))
+		*out_flags |= GS_APP_QUIRK_COMPULSORY;
+	return *out_flags != 0;
+}
+
 static gboolean
 refine_app (GsPlugin             *plugin,
 	    GsApp                *app,
 	    GsPluginRefineFlags   flags,
 	    GHashTable		 *repos,
-	    GPtrArray		 *wildcards,
+	    GPtrArray		 *provenance_wildcards,
+	    GPtrArray		 *compulsory_wildcards,
 	    GCancellable         *cancellable,
 	    GError              **error)
 {
 	const gchar *origin;
+	guint quirks;
 
 	/* not required */
 	if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0)
@@ -108,9 +184,8 @@ refine_app (GsPlugin             *plugin,
 
 	/* simple case */
 	origin = gs_app_get_origin (app);
-	if (origin != NULL && (g_hash_table_contains (repos, origin) ||
-	    (wildcards != NULL && gs_utils_strv_fnmatch ((gchar **) wildcards->pdata, origin)))) {
-		gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
+	if (gs_plugin_provenance_find_repo_flags (repos, provenance_wildcards, compulsory_wildcards, origin, &quirks)) {
+		gs_plugin_provenance_add_quirks (app, quirks);
 		return TRUE;
 	}
 
@@ -118,10 +193,9 @@ refine_app (GsPlugin             *plugin,
 	 * provenance quirk to the system-configured repositories (but not
 	 * user-configured ones). */
 	if (gs_app_get_kind (app) == AS_COMPONENT_KIND_REPOSITORY &&
-	    (g_hash_table_contains (repos, gs_app_get_id (app)) ||
-	    (wildcards != NULL && gs_utils_strv_fnmatch ((gchar **) wildcards->pdata, gs_app_get_id (app))))) {
+	    gs_plugin_provenance_find_repo_flags (repos, provenance_wildcards, compulsory_wildcards, gs_app_get_id (app), &quirks)) {
 		if (gs_app_get_scope (app) != AS_COMPONENT_SCOPE_USER)
-			gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
+			gs_plugin_provenance_add_quirks (app, quirks);
 		return TRUE;
 	}
 
@@ -134,11 +208,9 @@ refine_app (GsPlugin             *plugin,
 		return TRUE;
 	if (g_str_has_prefix (origin + 1, "installed:"))
 		origin += 10;
-	if (g_hash_table_contains (repos, origin + 1) ||
-	    (wildcards != NULL && gs_utils_strv_fnmatch ((gchar **) wildcards->pdata, origin + 1))) {
-		gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
-		return TRUE;
-	}
+	if (gs_plugin_provenance_find_repo_flags (repos, provenance_wildcards, compulsory_wildcards, origin + 1, &quirks))
+		gs_plugin_provenance_add_quirks (app, quirks);
+
 	return TRUE;
 }
 
@@ -151,20 +223,22 @@ gs_plugin_refine (GsPlugin             *plugin,
 {
 	GsPluginData *priv = gs_plugin_get_data (plugin);
 	g_autoptr(GHashTable) repos = NULL;
-	g_autoptr(GPtrArray) wildcards = NULL;
+	g_autoptr(GPtrArray) provenance_wildcards = NULL;
+	g_autoptr(GPtrArray) compulsory_wildcards = NULL;
 
 	/* nothing to do here */
 	if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE) == 0)
 		return TRUE;
 	repos = g_hash_table_ref (priv->repos);
-	wildcards = priv->wildcards != NULL ? g_ptr_array_ref (priv->wildcards) : NULL;
+	provenance_wildcards = priv->provenance_wildcards != NULL ? g_ptr_array_ref (priv->provenance_wildcards) : NULL;
+	compulsory_wildcards = priv->compulsory_wildcards != NULL ? g_ptr_array_ref (priv->compulsory_wildcards) : NULL;
 	/* nothing to search */
-	if (g_hash_table_size (repos) == 0)
+	if (g_hash_table_size (repos) == 0 && provenance_wildcards == NULL && compulsory_wildcards == NULL)
 		return TRUE;
 
 	for (guint i = 0; i < gs_app_list_length (list); i++) {
 		GsApp *app = gs_app_list_index (list, i);
-		if (!refine_app (plugin, app, flags, repos, wildcards, cancellable, error))
+		if (!refine_app (plugin, app, flags, repos, provenance_wildcards, compulsory_wildcards, cancellable, error))
 			return FALSE;
 	}
 
-- 
GitLab