Blob Blame History Raw
From 6edc7e0cfc672fe4c2331ac8131ea1aa82881b63 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Wed, 19 Dec 2018 19:58:16 -0500
Subject: [PATCH 1/9] settings: Make the keyfile backend parameterless

Make it possible to instantiate a keyfile settings backend
without specifying parameters, by turning the arguments to
the new() function into construct-only properties. If no
filename is specified, default to
$XDG_CONFIG_HOME/glib-2.0/settings/keyfile
---
 gio/gkeyfilesettingsbackend.c  | 252 ++++++++++++++++++++++++++-------
 gio/gsettingsbackendinternal.h |   2 +
 2 files changed, 199 insertions(+), 55 deletions(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index a37978e83..e8e9f44a5 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -21,6 +21,9 @@
 
 #include "config.h"
 
+#include <glib.h>
+#include <glibintl.h>
+
 #include <stdio.h>
 #include <string.h>
 
@@ -28,7 +31,8 @@
 #include "gfileinfo.h"
 #include "gfilemonitor.h"
 #include "gsimplepermission.h"
-#include "gsettingsbackend.h"
+#include "gsettingsbackendinternal.h"
+#include "giomodule.h"
 
 
 #define G_TYPE_KEYFILE_SETTINGS_BACKEND      (g_keyfile_settings_backend_get_type ())
@@ -41,6 +45,12 @@
 
 typedef GSettingsBackendClass GKeyfileSettingsBackendClass;
 
+enum {
+  PROP_FILENAME = 1,
+  PROP_ROOT_PATH,
+  PROP_ROOT_GROUP
+};
+
 typedef struct
 {
   GSettingsBackend   parent_instance;
@@ -61,7 +71,6 @@ typedef struct
   GFileMonitor      *dir_monitor;
 } GKeyfileSettingsBackend;
 
-static GType g_keyfile_settings_backend_get_type (void);
 G_DEFINE_TYPE (GKeyfileSettingsBackend,
                g_keyfile_settings_backend,
                G_TYPE_SETTINGS_BACKEND)
@@ -287,7 +296,8 @@ g_keyfile_settings_backend_check_one (gpointer key,
 {
   WriteManyData *data = user_data;
 
-  return data->failed = !path_is_valid (data->kfsb, key);
+  return data->failed = g_hash_table_contains (data->kfsb->system_locks, key) ||
+                        !path_is_valid (data->kfsb, key);
 }
 
 static gboolean
@@ -522,25 +532,6 @@ g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb)
 {
 }
 
-static void
-g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (class);
-
-  object_class->finalize = g_keyfile_settings_backend_finalize;
-
-  class->read = g_keyfile_settings_backend_read;
-  class->write = g_keyfile_settings_backend_write;
-  class->write_tree = g_keyfile_settings_backend_write_tree;
-  class->reset = g_keyfile_settings_backend_reset;
-  class->get_writable = g_keyfile_settings_backend_get_writable;
-  class->get_permission = g_keyfile_settings_backend_get_permission;
-  /* No need to implement subscribed/unsubscribe: the only point would be to
-   * stop monitoring the file when there's no GSettings anymore, which is no
-   * big win.
-   */
-}
-
 static void
 file_changed (GFileMonitor      *monitor,
               GFile             *file,
@@ -567,6 +558,185 @@ dir_changed (GFileMonitor       *monitor,
   g_keyfile_settings_backend_keyfile_writable (kfsb);
 }
 
+static void
+g_keyfile_settings_backend_constructed (GObject *object)
+{
+  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
+
+  if (kfsb->file == NULL)
+    {
+      char *filename = g_build_filename (g_get_user_config_dir (),
+                                         "glib-2.0", "settings", "keyfile",
+                                         NULL);
+      kfsb->file = g_file_new_for_path (filename);
+      g_free (filename);
+    }
+
+  if (kfsb->prefix == NULL)
+    {
+      kfsb->prefix = g_strdup ("/");
+      kfsb->prefix_len = 1;
+    }
+  
+  kfsb->keyfile = g_key_file_new ();
+  kfsb->permission = g_simple_permission_new (TRUE);
+
+  kfsb->dir = g_file_get_parent (kfsb->file);
+  g_file_make_directory_with_parents (kfsb->dir, NULL, NULL);
+
+  kfsb->file_monitor = g_file_monitor (kfsb->file, G_FILE_MONITOR_NONE, NULL, NULL);
+  kfsb->dir_monitor = g_file_monitor (kfsb->dir, G_FILE_MONITOR_NONE, NULL, NULL);
+
+  compute_checksum (kfsb->digest, NULL, 0);
+
+  g_signal_connect (kfsb->file_monitor, "changed",
+                    G_CALLBACK (file_changed), kfsb);
+  g_signal_connect (kfsb->dir_monitor, "changed",
+                    G_CALLBACK (dir_changed), kfsb);
+
+  g_keyfile_settings_backend_keyfile_writable (kfsb);
+  g_keyfile_settings_backend_keyfile_reload (kfsb);
+}
+
+static void
+g_keyfile_settings_backend_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILENAME:
+      /* Construct only. */
+      g_assert (kfsb->file == NULL);
+      kfsb->file = g_file_new_for_path (g_value_get_string (value));
+      break;
+
+    case PROP_ROOT_PATH:
+      /* Construct only. */
+      g_assert (kfsb->prefix == NULL);
+      kfsb->prefix = g_value_dup_string (value);
+      if (kfsb->prefix)
+        kfsb->prefix_len = strlen (kfsb->prefix);
+      break;
+
+    case PROP_ROOT_GROUP:
+      /* Construct only. */
+      g_assert (kfsb->root_group == NULL);
+      kfsb->root_group = g_value_dup_string (value);
+      if (kfsb->root_group)
+        kfsb->root_group_len = strlen (kfsb->root_group);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_keyfile_settings_backend_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
+
+  switch (prop_id)
+    {
+    case PROP_FILENAME:
+      g_value_set_string (value, g_file_peek_path (kfsb->file));
+      break;
+
+    case PROP_ROOT_PATH:
+      g_value_set_string (value, kfsb->prefix);
+      break;
+
+    case PROP_ROOT_GROUP:
+      g_value_set_string (value, kfsb->root_group);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = g_keyfile_settings_backend_finalize;
+  object_class->constructed = g_keyfile_settings_backend_constructed;
+  object_class->get_property = g_keyfile_settings_backend_get_property;
+  object_class->set_property = g_keyfile_settings_backend_set_property;
+
+  class->read = g_keyfile_settings_backend_read;
+  class->write = g_keyfile_settings_backend_write;
+  class->write_tree = g_keyfile_settings_backend_write_tree;
+  class->reset = g_keyfile_settings_backend_reset;
+  class->get_writable = g_keyfile_settings_backend_get_writable;
+  class->get_permission = g_keyfile_settings_backend_get_permission;
+  /* No need to implement subscribed/unsubscribe: the only point would be to
+   * stop monitoring the file when there's no GSettings anymore, which is no
+   * big win.
+   */
+
+  /**
+   * GKeyfileSettingsBackend:filename:
+   *
+   * The location where the settings are stored on disk.
+   *
+   * Defaults to `$XDG_CONFIG_HOME/glib-2.0/settings/keyfile`.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_FILENAME,
+                                   g_param_spec_string ("filename",
+                                                        P_("Filename"),
+                                                        P_("The filename"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GKeyfileSettingsBackend:root-path:
+   *
+   * All settings read to or written from the backend must fall under the
+   * path given in @root_path (which must start and end with a slash and
+   * not contain two consecutive slashes).  @root_path may be "/".
+   * 
+   * Defaults to "/".
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ROOT_PATH,
+                                   g_param_spec_string ("root-path",
+                                                        P_("Root path"),
+                                                        P_("The root path"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GKeyfileSettingsBackend:root-group:
+   *
+   * If @root_group is non-%NULL then it specifies the name of the keyfile
+   * group used for keys that are written directly below the root path.
+   *
+   * Defaults to NULL.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ROOT_GROUP,
+                                   g_param_spec_string ("root-group",
+                                                        P_("Root group"),
+                                                        P_("The root group"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+}
+
 /**
  * g_keyfile_settings_backend_new:
  * @filename: the filename of the keyfile
@@ -626,43 +796,15 @@ g_keyfile_settings_backend_new (const gchar *filename,
                                 const gchar *root_path,
                                 const gchar *root_group)
 {
-  GKeyfileSettingsBackend *kfsb;
-
   g_return_val_if_fail (filename != NULL, NULL);
   g_return_val_if_fail (root_path != NULL, NULL);
   g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL);
   g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL);
   g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL);
 
-  kfsb = g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, NULL);
-  kfsb->keyfile = g_key_file_new ();
-  kfsb->permission = g_simple_permission_new (TRUE);
-
-  kfsb->file = g_file_new_for_path (filename);
-  kfsb->dir = g_file_get_parent (kfsb->file);
-  g_file_make_directory_with_parents (kfsb->dir, NULL, NULL);
-
-  kfsb->file_monitor = g_file_monitor (kfsb->file, 0, NULL, NULL);
-  kfsb->dir_monitor = g_file_monitor (kfsb->dir, 0, NULL, NULL);
-
-  kfsb->prefix_len = strlen (root_path);
-  kfsb->prefix = g_strdup (root_path);
-
-  if (root_group)
-    {
-      kfsb->root_group_len = strlen (root_group);
-      kfsb->root_group = g_strdup (root_group);
-    }
-
-  compute_checksum (kfsb->digest, NULL, 0);
-
-  g_signal_connect (kfsb->file_monitor, "changed",
-                    G_CALLBACK (file_changed), kfsb);
-  g_signal_connect (kfsb->dir_monitor, "changed",
-                    G_CALLBACK (dir_changed), kfsb);
-
-  g_keyfile_settings_backend_keyfile_writable (kfsb);
-  g_keyfile_settings_backend_keyfile_reload (kfsb);
-
-  return G_SETTINGS_BACKEND (kfsb);
+  return G_SETTINGS_BACKEND (g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND,
+                                           "filename", filename,
+                                           "root-path", root_path,
+                                           "root-group", root_group,
+                                           NULL));
 }
diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h
index 2a76a80bc..9e1d51dba 100644
--- a/gio/gsettingsbackendinternal.h
+++ b/gio/gsettingsbackendinternal.h
@@ -87,6 +87,8 @@ GType                   g_null_settings_backend_get_type                (void);
 
 GType                   g_memory_settings_backend_get_type              (void);
 
+GType                   g_keyfile_settings_backend_get_type             (void);
+
 #ifdef HAVE_COCOA
 GType                   g_nextstep_settings_backend_get_type            (void);
 #endif
-- 
2.28.0


From cd448d51f610f1e6a701ac8a1146d3b2048427a0 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Wed, 19 Dec 2018 20:03:29 -0500
Subject: [PATCH 2/9] settings: Register the keyfile backend as extension

This was not done previously because the backend
could not be instantiated without parameters.
---
 gio/gkeyfilesettingsbackend.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index e8e9f44a5..0e9789cde 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -32,7 +32,7 @@
 #include "gfilemonitor.h"
 #include "gsimplepermission.h"
 #include "gsettingsbackendinternal.h"
-#include "giomodule.h"
+#include "giomodule-priv.h"
 
 
 #define G_TYPE_KEYFILE_SETTINGS_BACKEND      (g_keyfile_settings_backend_get_type ())
@@ -71,9 +71,12 @@ typedef struct
   GFileMonitor      *dir_monitor;
 } GKeyfileSettingsBackend;
 
-G_DEFINE_TYPE (GKeyfileSettingsBackend,
-               g_keyfile_settings_backend,
-               G_TYPE_SETTINGS_BACKEND)
+G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend,
+                         g_keyfile_settings_backend,
+                         G_TYPE_SETTINGS_BACKEND,
+                         _g_io_modules_ensure_extension_points_registered ();
+                         g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
+                                                         g_define_type_id, "keyfile", 10))
 
 static void
 compute_checksum (guint8        *digest,
-- 
2.28.0


From 8cbdb7dd496f6c2361eb46ca4c0848d7210fe27a Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Mon, 5 Nov 2018 16:07:55 -0500
Subject: [PATCH 3/9] settings: Add support for defaults to keyfile backend

Stacked databases and locks are dconf features that allow
management software like Fleet Commander to set system-wide
defaults and overrides centrally for applications.

This patch adds minimal support for the same to the keyfile
backend. We look for a keyfile named 'defaults' and a
lock-list named 'locks'.

Suitable files can be produced from a dconf database with
dconf dump and dconf list-locks, respectively.

The default location for these files is /etc/glib-2.0/settings/.
For test purposes, this can be overwritten with the
GSETTINGS_DEFAULTS_DIR environment variable.

Writes always go to the per-user keyfile.
---
 gio/gkeyfilesettingsbackend.c | 138 ++++++++++++++++++++++++++++++++--
 1 file changed, 132 insertions(+), 6 deletions(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index 0e9789cde..0a4e81511 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -29,6 +29,7 @@
 
 #include "gfile.h"
 #include "gfileinfo.h"
+#include "gfileenumerator.h"
 #include "gfilemonitor.h"
 #include "gsimplepermission.h"
 #include "gsettingsbackendinternal.h"
@@ -45,11 +46,12 @@
 
 typedef GSettingsBackendClass GKeyfileSettingsBackendClass;
 
-enum {
+typedef enum {
   PROP_FILENAME = 1,
   PROP_ROOT_PATH,
-  PROP_ROOT_GROUP
-};
+  PROP_ROOT_GROUP,
+  PROP_DEFAULTS_DIR
+} GKeyfileSettingsBackendProperty;
 
 typedef struct
 {
@@ -58,6 +60,9 @@ typedef struct
   GKeyFile          *keyfile;
   GPermission       *permission;
   gboolean           writable;
+  char              *defaults_dir;
+  GKeyFile          *system_keyfile;
+  GHashTable        *system_locks; /* Used as a set, owning the strings it contains */
 
   gchar             *prefix;
   gint               prefix_len;
@@ -196,10 +201,19 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb,
   if (convert_path (kfsb, key, &group, &name))
     {
       gchar *str;
+      gchar *sysstr;
 
       g_assert (*name);
 
+      sysstr = g_key_file_get_value (kfsb->system_keyfile, group, name, NULL);
       str = g_key_file_get_value (kfsb->keyfile, group, name, NULL);
+      if (sysstr &&
+          (g_hash_table_contains (kfsb->system_locks, key) ||
+           str == NULL))
+        {
+          g_free (str);
+          str = g_steal_pointer (&sysstr);
+        }
 
       if (str)
         {
@@ -207,6 +221,8 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb,
           g_free (str);
         }
 
+      g_free (sysstr);
+
       g_free (group);
       g_free (name);
     }
@@ -221,6 +237,9 @@ set_to_keyfile (GKeyfileSettingsBackend *kfsb,
 {
   gchar *group, *name;
 
+  if (g_hash_table_contains (kfsb->system_locks, key))
+    return FALSE;
+
   if (convert_path (kfsb, key, &group, &name))
     {
       if (value)
@@ -368,7 +387,9 @@ g_keyfile_settings_backend_get_writable (GSettingsBackend *backend,
 {
   GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend);
 
-  return kfsb->writable && path_is_valid (kfsb, name);
+  return kfsb->writable &&
+         !g_hash_table_contains (kfsb->system_locks, name) &&
+         path_is_valid (kfsb, name);
 }
 
 static GPermission *
@@ -514,6 +535,9 @@ g_keyfile_settings_backend_finalize (GObject *object)
 
   g_key_file_free (kfsb->keyfile);
   g_object_unref (kfsb->permission);
+  g_key_file_unref (kfsb->system_keyfile);
+  g_hash_table_unref (kfsb->system_locks);
+  g_free (kfsb->defaults_dir);
 
   g_file_monitor_cancel (kfsb->file_monitor);
   g_object_unref (kfsb->file_monitor);
@@ -561,6 +585,75 @@ dir_changed (GFileMonitor       *monitor,
   g_keyfile_settings_backend_keyfile_writable (kfsb);
 }
 
+static void
+load_system_settings (GKeyfileSettingsBackend *kfsb)
+{
+  GError *error = NULL;
+  const char *dir = "/etc/glib-2.0/settings";
+  char *path;
+  char *contents;
+
+  kfsb->system_keyfile = g_key_file_new ();
+  kfsb->system_locks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  if (kfsb->defaults_dir)
+    dir = kfsb->defaults_dir;
+
+  path = g_build_filename (dir, "defaults", NULL);
+
+  /* The defaults are in the same keyfile format that we use for the settings.
+   * It can be produced from a dconf database using: dconf dump
+   */
+  if (!g_key_file_load_from_file (kfsb->system_keyfile, path, G_KEY_FILE_NONE, &error))
+    {
+      if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+        g_warning ("Failed to read %s: %s", path, error->message);
+      g_clear_error (&error);
+    }
+  else
+    g_debug ("Loading default settings from %s", path);
+
+  g_free (path);
+
+  path = g_build_filename (dir, "locks", NULL);
+
+  /* The locks file is a text file containing a list paths to lock, one per line.
+   * It can be produced from a dconf database using: dconf list-locks
+   */
+  if (!g_file_get_contents (path, &contents, NULL, &error))
+    {
+      if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+        g_warning ("Failed to read %s: %s", path, error->message);
+      g_clear_error (&error);
+    }
+  else
+    {
+      char **lines;
+      gsize i;
+
+      g_debug ("Loading locks from %s", path);
+
+      lines = g_strsplit (contents, "\n", 0);
+      for (i = 0; lines[i]; i++)
+        {
+          char *line = lines[i];
+          if (line[0] == '#' || line[0] == '\0')
+            {
+              g_free (line);
+              continue;
+            }
+
+          g_debug ("Locking key %s", line);
+          g_hash_table_add (kfsb->system_locks, g_steal_pointer (&line));
+        }
+
+      g_free (lines);
+    }
+  g_free (contents);
+
+  g_free (path);
+}
+
 static void
 g_keyfile_settings_backend_constructed (GObject *object)
 {
@@ -599,6 +692,8 @@ g_keyfile_settings_backend_constructed (GObject *object)
 
   g_keyfile_settings_backend_keyfile_writable (kfsb);
   g_keyfile_settings_backend_keyfile_reload (kfsb);
+
+  load_system_settings (kfsb);
 }
 
 static void
@@ -609,7 +704,7 @@ g_keyfile_settings_backend_set_property (GObject      *object,
 {
   GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
 
-  switch (prop_id)
+  switch ((GKeyfileSettingsBackendProperty)prop_id)
     {
     case PROP_FILENAME:
       /* Construct only. */
@@ -633,6 +728,12 @@ g_keyfile_settings_backend_set_property (GObject      *object,
         kfsb->root_group_len = strlen (kfsb->root_group);
       break;
 
+    case PROP_DEFAULTS_DIR:
+      /* Construct only. */
+      g_assert (kfsb->defaults_dir == NULL);
+      kfsb->defaults_dir = g_value_dup_string (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -647,7 +748,7 @@ g_keyfile_settings_backend_get_property (GObject    *object,
 {
   GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object);
 
-  switch (prop_id)
+  switch ((GKeyfileSettingsBackendProperty)prop_id)
     {
     case PROP_FILENAME:
       g_value_set_string (value, g_file_peek_path (kfsb->file));
@@ -661,6 +762,10 @@ g_keyfile_settings_backend_get_property (GObject    *object,
       g_value_set_string (value, kfsb->root_group);
       break;
 
+    case PROP_DEFAULTS_DIR:
+      g_value_set_string (value, kfsb->defaults_dir);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -738,6 +843,22 @@ g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
                                                         NULL,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GKeyfileSettingsBackend:default-dir:
+   *
+   * The directory where the system defaults and locks are located.
+   *
+   * Defaults to `/etc/glib-2.0/settings`.
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_DEFAULTS_DIR,
+                                   g_param_spec_string ("defaults-dir",
+                                                        P_("Default dir"),
+                                                        P_("Defaults dir"),
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -792,6 +913,11 @@ g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class)
  * characters in your path names or '=' in your key names you may be in
  * trouble.
  *
+ * The backend reads default values from a keyfile called `defaults` in
+ * the directory specified by the #GKeyfileSettingsBackend:defaults-dir property,
+ * and a list of locked keys from a text file with the name `locks` in
+ * the same location.
+ *
  * Returns: (transfer full): a keyfile-backed #GSettingsBackend
  **/
 GSettingsBackend *
-- 
2.28.0


From 93dceb39fe2de23aeffe40ff02c824393c749270 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Fri, 2 Nov 2018 23:00:49 -0400
Subject: [PATCH 4/9] settings: Prefer the keyfile backend when sandboxed

When we are in a sandboxed situation, bump the priority
of the keyfile settings backend above the dconf one,
so we use a keyfile inside the sandbox instead of requiring
holes in the sandbox for dconf.
---
 gio/gkeyfilesettingsbackend.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index 0a4e81511..398cb053a 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -34,6 +34,7 @@
 #include "gsimplepermission.h"
 #include "gsettingsbackendinternal.h"
 #include "giomodule-priv.h"
+#include "gportalsupport.h"
 
 
 #define G_TYPE_KEYFILE_SETTINGS_BACKEND      (g_keyfile_settings_backend_get_type ())
@@ -76,12 +77,18 @@ typedef struct
   GFileMonitor      *dir_monitor;
 } GKeyfileSettingsBackend;
 
+#ifdef G_OS_WIN32
+#define EXTENSION_PRIORITY 10
+#else
+#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10)
+#endif
+
 G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend,
                          g_keyfile_settings_backend,
                          G_TYPE_SETTINGS_BACKEND,
                          _g_io_modules_ensure_extension_points_registered ();
                          g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
-                                                         g_define_type_id, "keyfile", 10))
+                                                         g_define_type_id, "keyfile", EXTENSION_PRIORITY))
 
 static void
 compute_checksum (guint8        *digest,
-- 
2.28.0


From 5d0d49750564aa7fd9e7d0d58c08a08847570921 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Mon, 21 Jan 2019 22:55:45 -0500
Subject: [PATCH 5/9] keyfile settings: Accept unquoted strings

It is hard for users to remember that strings have to be explicitly
quoted in the keyfile. Be lenient and accept strings that lack those
quotes.
---
 gio/gkeyfilesettingsbackend.c | 19 +++++++++++++++++++
 gio/tests/gsettings.c         | 17 +++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index 398cb053a..d5796b706 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -225,6 +225,25 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb,
       if (str)
         {
           return_value = g_variant_parse (type, str, NULL, NULL, NULL);
+          if (return_value == NULL &&
+              g_variant_type_equal (type, G_VARIANT_TYPE_STRING) &&
+              str[0] != '\"')
+            {
+              GString *s = g_string_sized_new (strlen (str) + 2);
+              char *p = str;
+
+              g_string_append_c (s, '\"');
+              while (*p)
+                {
+                  if (*p == '\"')
+                    g_string_append_c (s, '\\');
+                  g_string_append_c (s, *p);
+                  p++;
+                }
+              g_string_append_c (s, '\"');
+              return_value = g_variant_parse (type, s->str, NULL, NULL, NULL);
+              g_string_free (s, TRUE);
+            }
           g_free (str);
         }
 
diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c
index 2be4122fe..6eb49f124 100644
--- a/gio/tests/gsettings.c
+++ b/gio/tests/gsettings.c
@@ -1716,6 +1716,23 @@ test_keyfile (void)
   g_assert_cmpstr (str, ==, "howdy");
   g_free (str);
 
+  /* Now check setting a string without quotes */
+  called = FALSE;
+  g_signal_connect (settings, "changed::greeting", G_CALLBACK (key_changed_cb), &called);
+
+  g_key_file_set_string (keyfile, "tests", "greeting", "he\"l🤗uń");
+  g_free (data);
+  data = g_key_file_to_data (keyfile, &len, NULL);
+  g_file_set_contents ("keyfile/gsettings.store", data, len, &error);
+  g_assert_no_error (error);
+  while (!called)
+    g_main_context_iteration (NULL, FALSE);
+  g_signal_handlers_disconnect_by_func (settings, key_changed_cb, &called);
+
+  str = g_settings_get_string (settings, "greeting");
+  g_assert_cmpstr (str, ==, "he\"l🤗uń");
+  g_free (str);
+
   g_settings_set (settings, "farewell", "s", "cheerio");
   
   called = FALSE;
-- 
2.28.0


From 3765e73f9a2dbbc6863bcffdee85ffb18530c89b Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Wed, 23 Jan 2019 15:14:58 +0000
Subject: [PATCH 6/9] gkeyfilesettingsbackend: Add a code comment to clarify
 things

Signed-off-by: Philip Withnall <withnall@endlessm.com>
---
 gio/gkeyfilesettingsbackend.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index d5796b706..5ea632305 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -225,6 +225,10 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb,
       if (str)
         {
           return_value = g_variant_parse (type, str, NULL, NULL, NULL);
+
+          /* As a special case, support values of type %G_VARIANT_TYPE_STRING
+           * not being quoted, since users keep forgetting to do it and then
+           * getting confused. */
           if (return_value == NULL &&
               g_variant_type_equal (type, G_VARIANT_TYPE_STRING) &&
               str[0] != '\"')
-- 
2.28.0


From 1df628861afe027e8dce5e27f4249059bad6bc60 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Wed, 10 Jul 2019 11:14:03 -0400
Subject: [PATCH 7/9] key file: Handle filename being NULL

This happens when we are default-constructed
without explicit arguments.

Closes: https://gitlab.gnome.org/GNOME/glib/issues/1825
---
 gio/gkeyfilesettingsbackend.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index 5ea632305..a874af287 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -739,7 +739,8 @@ g_keyfile_settings_backend_set_property (GObject      *object,
     case PROP_FILENAME:
       /* Construct only. */
       g_assert (kfsb->file == NULL);
-      kfsb->file = g_file_new_for_path (g_value_get_string (value));
+      if (g_value_get_string (value))
+        kfsb->file = g_file_new_for_path (g_value_get_string (value));
       break;
 
     case PROP_ROOT_PATH:
-- 
2.28.0


From 9c5d3a6081e5ff419db96d9651bddbfcdb8b1bc6 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Fri, 12 Jul 2019 11:30:30 -0400
Subject: [PATCH] portal: Add a getter for dconf access

Add method to find whether the sandbox provides
access to dconf. This will be used to tweak
the priorities for the keyfile settings backend.
---
 gio/gportalsupport.c | 18 ++++++++++++++++++
 gio/gportalsupport.h |  1 +
 2 files changed, 19 insertions(+)

diff --git a/gio/gportalsupport.c b/gio/gportalsupport.c
index 2f1e82517..b0a94b360 100644
--- a/gio/gportalsupport.c
+++ b/gio/gportalsupport.c
@@ -23,6 +23,7 @@
 static gboolean flatpak_info_read;
 static gboolean use_portal;
 static gboolean network_available;
+static gboolean dconf_access;
 
 static void
 read_flatpak_info (void)
@@ -40,11 +41,13 @@ read_flatpak_info (void)
 
       use_portal = TRUE;
       network_available = FALSE;
+      dconf_access = FALSE;
 
       keyfile = g_key_file_new ();
       if (g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL))
         {
           char **shared = NULL;
+          char *dconf_policy = NULL;
 
           shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL);
           if (shared)
@@ -52,6 +55,14 @@ read_flatpak_info (void)
               network_available = g_strv_contains ((const char * const *)shared, "network");
               g_strfreev (shared);
             }
+
+          dconf_policy = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL);
+          if (dconf_policy)
+            {
+              if (strcmp (dconf_policy, "talk") == 0)
+                dconf_access = TRUE;
+              g_free (dconf_policy);
+            }
         }
 
       g_key_file_unref (keyfile);
@@ -64,6 +75,7 @@ read_flatpak_info (void)
       if (var && var[0] == '1')
         use_portal = TRUE;
       network_available = TRUE;
+      dconf_access = TRUE;
     }
 }
 
@@ -81,3 +93,9 @@ glib_network_available_in_sandbox (void)
   return network_available;
 }
 
+gboolean
+glib_has_dconf_access_in_sandbox (void)
+{
+  read_flatpak_info ();
+  return dconf_access;
+}
diff --git a/gio/gportalsupport.h b/gio/gportalsupport.h
index a331f45d3..746f1fd6b 100644
--- a/gio/gportalsupport.h
+++ b/gio/gportalsupport.h
@@ -24,6 +24,7 @@ G_BEGIN_DECLS
 
 gboolean glib_should_use_portal (void);
 gboolean glib_network_available_in_sandbox (void);
+gboolean glib_has_dconf_access_in_sandbox (void);
 
 G_END_DECLS
 
-- 
GitLab


From e6461f208931e27b52d82f7d3b5a7ce4fc26c9d7 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Fri, 12 Jul 2019 11:31:37 -0400
Subject: [PATCH 8/9] settings: Tweak priorities for keyfile backend

We want to use the keyfile backend in sandboxes,
but we want to avoid people losing their existing
settings that are stored in dconf. Flatpak does
a migration from dconf to keyfile, but only if
the app explictly requests it.

From an app perspective, there are two steps to
the dconf->keyfile migration:
1. Request that flatpak do the migration, by adding
   the migrate-path key to the metadata
2. Stop adding the 'dconf hole' to the sandbox

To keep us from switching to the keyfile backend
prematurely, look at whether the app has stopped
requesting a 'dconf hole' in the sandbox.
---
 gio/gkeyfilesettingsbackend.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c
index a874af287..f5358818e 100644
--- a/gio/gkeyfilesettingsbackend.c
+++ b/gio/gkeyfilesettingsbackend.c
@@ -80,7 +80,7 @@ typedef struct
 #ifdef G_OS_WIN32
 #define EXTENSION_PRIORITY 10
 #else
-#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10)
+#define EXTENSION_PRIORITY (glib_should_use_portal () && !glib_has_dconf_access_in_sandbox () ? 110 : 10)
 #endif
 
 G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend,
-- 
2.28.0


From 5f8d787815dc220fa2e288e03cd0cb9497727753 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Mon, 8 Jul 2019 10:31:51 -0400
Subject: [PATCH] Ensure that the keyfile settings backend exists

We need to bring the type into existence.

Closes: https://gitlab.gnome.org/GNOME/glib/issues/1822
---
 gio/giomodule.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gio/giomodule.c b/gio/giomodule.c
index 9bb28985a..1007abdbf 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -1211,6 +1211,7 @@ _g_io_modules_ensure_loaded (void)
       /* Initialize types from built-in "modules" */
       g_type_ensure (g_null_settings_backend_get_type ());
       g_type_ensure (g_memory_settings_backend_get_type ());
+      g_type_ensure (g_keyfile_settings_backend_get_type ());
 #if defined(HAVE_INOTIFY_INIT1)
       g_type_ensure (g_inotify_file_monitor_get_type ());
 #endif
-- 
GitLab