[dconf] client-side lockdown support



commit a2c4601f2650e83bff2fec3f1a13dc46cdf3a070
Author: Ryan Lortie <desrt desrt ca>
Date:   Fri May 6 12:10:23 2011 +0200

    client-side lockdown support
    
    Support lockdown on the client side for GSettings.
    
    For performance reasons only lockdown of specific keys (not entire
    subpaths) is supported at the moment.  This may change in the future if
    we can find a way to make large numbers of related GVDB lookups
    sufficiently performant.

 engine/dconf-engine.c            |  126 +++++++++++++++++++++++++++-----------
 engine/dconf-engine.h            |    6 ++
 gsettings/dconfsettingsbackend.c |   18 ++++++
 3 files changed, 114 insertions(+), 36 deletions(-)
---
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
index 9181fc2..2878b06 100644
--- a/engine/dconf-engine.c
+++ b/engine/dconf-engine.c
@@ -128,6 +128,7 @@ struct _DConfEngine
   guint8     *shm;
 
   GvdbTable **gvdbs;
+  GvdbTable **lock_tables;
   gchar     **object_paths;
   gchar      *bus_types;
   gchar     **names;
@@ -230,6 +231,8 @@ dconf_engine_refresh_system (DConfEngine *engine)
           if (engine->gvdbs[i] == NULL)
             g_error ("Unable to open '%s', specified in dconf profile\n",
                      filename);
+          engine->lock_tables[i] = gvdb_table_get_table (engine->gvdbs[i],
+                                                         ".locks");
           g_free (filename);
           engine->state++;
         }
@@ -360,6 +363,7 @@ dconf_engine_new (const gchar *profile)
 
   engine->object_paths = g_new (gchar *, engine->n_dbs);
   engine->gvdbs = g_new0 (GvdbTable *, engine->n_dbs);
+  engine->lock_tables = g_new0 (GvdbTable *, engine->n_dbs);
   engine->bus_types = g_strdup ("eyyyyyyyyyyyyy");
   engine->state = 0;
 
@@ -390,6 +394,9 @@ dconf_engine_free (DConfEngine *engine)
 
       if (engine->gvdbs[i])
         gvdb_table_unref (engine->gvdbs[i]);
+
+      if (engine->lock_tables[i])
+        gvdb_table_unref (engine->lock_tables[i]);
     }
 
   if (engine->shm)
@@ -403,67 +410,76 @@ dconf_engine_free (DConfEngine *engine)
   g_free (engine->bus_types);
   g_free (engine->names);
   g_free (engine->gvdbs);
+  g_free (engine->lock_tables);
 
   g_slice_free (DConfEngine, engine);
 }
 
-GVariant *
-dconf_engine_read (DConfEngine  *engine,
-                   const gchar  *key)
+static GVariant *
+dconf_engine_read_internal (DConfEngine  *engine,
+                            const gchar  *key,
+                            gboolean      user,
+                            gboolean      system)
 {
   GVariant *value = NULL;
+  gint lowest;
+  gint limit;
   gint i;
 
   g_static_mutex_lock (&engine->lock);
 
   dconf_engine_refresh (engine);
 
-  if (engine->gvdbs[0])
-    value = gvdb_table_get_value (engine->gvdbs[0], key);
-
-  for (i = 1; i < engine->n_dbs && value == NULL; i++)
-    value = gvdb_table_get_value (engine->gvdbs[i], key);
+  /* Bound the search space depending on the databases that we are
+   * interested in.
+   */
+  limit = system ? engine->n_dbs : 1;
+  lowest = user ? 0 : 1;
+
+  /* We want i equal to the index of the highest database containing a
+   * lock, or i == lowest if there is no lock.  For that reason, we
+   * don't actually check the lowest database for a lock.  That makes
+   * sense, because even if it had a lock, it would not change our
+   * search policy (which would be to check the lowest one first).
+   *
+   * Note that we intentionally dishonour 'limit' here -- we want to
+   * ensure that values in the user database are always ignored when
+   * locks are present.
+   */
+  for (i = engine->n_dbs - 1; lowest < i; i--)
+    if (engine->lock_tables[i] != NULL &&
+        gvdb_table_has_value (engine->lock_tables[i], key))
+      break;
+
+  while (i < limit && value == NULL)
+    value = gvdb_table_get_value (engine->gvdbs[i++], key);
 
   g_static_mutex_unlock (&engine->lock);
 
+  g_print ("you read %s, i say %s\n", key, value ? g_variant_print (value, TRUE) : "(null)");
+
   return value;
 }
 
 GVariant *
+dconf_engine_read (DConfEngine  *engine,
+                   const gchar  *key)
+{
+  return dconf_engine_read_internal (engine, key, TRUE, TRUE);
+}
+
+GVariant *
 dconf_engine_read_default (DConfEngine  *engine,
                            const gchar  *key)
 {
-  GVariant *value = NULL;
-  gint i;
-
-  g_static_mutex_lock (&engine->lock);
-
-  dconf_engine_refresh (engine);
-
-  for (i = 1; i < engine->n_dbs && value == NULL; i++)
-    value = gvdb_table_get_value (engine->gvdbs[i], key);
-
-  g_static_mutex_unlock (&engine->lock);
-
-  return value;
+  return dconf_engine_read_internal (engine, key, FALSE, TRUE);
 }
 
 GVariant *
 dconf_engine_read_no_default (DConfEngine  *engine,
                               const gchar  *key)
 {
-  GVariant *value = NULL;
-
-  g_static_mutex_lock (&engine->lock);
-
-  dconf_engine_refresh (engine);
-
-  if (engine->gvdbs[0])
-    value = gvdb_table_get_value (engine->gvdbs[0], key);
-
-  g_static_mutex_unlock (&engine->lock);
-
-  return value;
+  return dconf_engine_read_internal (engine, key, TRUE, FALSE);
 }
 
 static void
@@ -518,10 +534,30 @@ dconf_engine_unwatch (DConfEngine        *engine,
 }
 
 gboolean
-dconf_engine_is_writable (DConfEngine         *engine,
-                          const gchar         *name)
+dconf_engine_is_writable (DConfEngine *engine,
+                          const gchar *name)
 {
-  return TRUE;
+  gboolean writable = TRUE;
+  gint i;
+
+  g_static_mutex_lock (&engine->lock);
+
+  dconf_engine_refresh (engine);
+
+  /* Don't check for locks in the user database.
+   * If there is only a user database then the loop won't run at all.
+   */
+  for (i = engine->n_dbs - 1; 0 < i; i--)
+    if (engine->lock_tables[i] != NULL &&
+        gvdb_table_has_value (engine->lock_tables[i], name))
+      {
+        writable = FALSE;
+        break;
+      }
+
+  g_static_mutex_unlock (&engine->lock);
+
+  return writable;
 }
 
 static GVariant *
@@ -672,6 +708,24 @@ dconf_engine_decode_notify (DConfEngine   *engine,
 }
 
 gboolean
+dconf_engine_decode_writability_notify (const gchar **path,
+                                        const gchar  *iface,
+                                        const gchar  *method,
+                                        GVariant     *body)
+{
+  if (strcmp (iface, "ca.desrt.dconf.Writer") ||
+      strcmp (method, "WritabilityNotify"))
+    return FALSE;
+
+  if (!g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
+    return FALSE;
+
+  g_variant_get_child (body, 0, "&s", path);
+
+  return TRUE;
+}
+
+gboolean
 dconf_engine_interpret_reply (DConfEngineMessage  *dcem,
                               const gchar         *sender,
                               GVariant            *body,
diff --git a/engine/dconf-engine.h b/engine/dconf-engine.h
index d2058d5..0bde8f0 100644
--- a/engine/dconf-engine.h
+++ b/engine/dconf-engine.h
@@ -144,6 +144,12 @@ gboolean                dconf_engine_decode_notify                      (DConfEn
                                                                          const gchar             *member,
                                                                          GVariant                *body);
 G_GNUC_INTERNAL
+gboolean                dconf_engine_decode_writability_notify          (const gchar            **path,
+                                                                         const gchar             *iface,
+                                                                         const gchar             *method,
+                                                                         GVariant                *body);
+
+G_GNUC_INTERNAL
 void                    dconf_engine_set_locked                         (DConfEngine             *engine,
                                                                          const gchar             *path,
                                                                          gboolean                 locked,
diff --git a/gsettings/dconfsettingsbackend.c b/gsettings/dconfsettingsbackend.c
index d978646..77d9358 100644
--- a/gsettings/dconfsettingsbackend.c
+++ b/gsettings/dconfsettingsbackend.c
@@ -101,6 +101,24 @@ dconf_settings_backend_signal (GDBusConnection *connection,
 
       g_free (rels);
     }
+
+  if (dconf_engine_decode_writability_notify (&path, interface_name,
+                                              signal_name, parameters))
+    {
+      GSettingsBackend *backend = G_SETTINGS_BACKEND (dcsb);
+
+      if (g_str_has_suffix (path, "/"))
+        {
+          g_settings_backend_path_writable_changed (backend, path);
+          g_settings_backend_path_changed (backend, path, NULL);
+        }
+
+      else
+        {
+          g_settings_backend_writable_changed (backend, path);
+          g_settings_backend_changed (backend, path, NULL);
+        }
+    }
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]