[dconf] regenerate database file when it fills



commit b129c5024c4e14ce44ad6f54e2cd27fb62a49654
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Aug 18 00:43:22 2009 -0400

    regenerate database file when it fills

 writer/dconf-dbus-writer.c    |   13 ++--
 writer/dconf-writer-file.c    |  169 +++++++++++++++++++++++++----------------
 writer/dconf-writer-flatten.c |  121 +++++++++++++++++++++++++++++
 writer/dconf-writer-merge.c   |    7 +-
 writer/dconf-writer-private.h |   10 +++
 writer/dconf-writer.c         |    7 +-
 writer/dconf-writer.h         |   11 ++-
 7 files changed, 252 insertions(+), 86 deletions(-)
---
diff --git a/writer/dconf-dbus-writer.c b/writer/dconf-dbus-writer.c
index defdafb..2eb33c9 100644
--- a/writer/dconf-dbus-writer.c
+++ b/writer/dconf-dbus-writer.c
@@ -251,8 +251,8 @@ dconf_dbus_writer_handle_message (DConfDBusWriter *writer)
       value = dconf_dbus_variant_to_gv (&iter);
       dbus_message_iter_next (&iter);
 
-      status = dconf_writer_set (writer->writer,
-                                 key, value, &error);
+      dconf_writer_set (writer->writer, key, value);
+      status = dconf_writer_sync (writer->writer, &error);
       g_variant_unref (value);
 
       if (status == TRUE)
@@ -298,10 +298,11 @@ dconf_dbus_writer_handle_message (DConfDBusWriter *writer)
 
       g_assert (names->len == values->len);
 
-      status = dconf_writer_merge (writer->writer, prefix,
-                                   (const gchar **) names->pdata,
-                                   (GVariant **) values->pdata,
-                                   names->len, &error);
+      dconf_writer_merge (writer->writer, prefix,
+                          (const gchar **) names->pdata,
+                          (GVariant **) values->pdata,
+                          names->len);
+      status = dconf_writer_sync (writer->writer, &error);
 
       for (i = 0; i < values->len; i++)
         g_variant_unref (values->pdata[i]);
diff --git a/writer/dconf-writer-file.c b/writer/dconf-writer-file.c
index 423e032..7824896 100644
--- a/writer/dconf-writer-file.c
+++ b/writer/dconf-writer-file.c
@@ -47,17 +47,17 @@ dconf_writer_create_directory (const gchar  *filename,
 
 static gpointer
 dconf_writer_create_temp_file (const gchar  *filename,
+                               gint         *fd,
                                gint          bytes,
                                gchar       **tmpname,
                                GError      **error)
 {
   gpointer contents;
   gint saved_errno;
-  gint fd;
 
   *tmpname = g_strdup_printf ("%s.XXXXXX", filename);
 
-  if ((fd = g_mkstemp (*tmpname)) < 0)
+  if ((*fd = g_mkstemp (*tmpname)) < 0)
     {
       saved_errno = errno;
 
@@ -70,7 +70,7 @@ dconf_writer_create_temp_file (const gchar  *filename,
       return NULL;
     }
 
-  if ((saved_errno = posix_fallocate (fd, 0, bytes)))
+  if ((saved_errno = posix_fallocate (*fd, 0, bytes)))
     {
       g_set_error (error, G_FILE_ERROR,
                    g_file_error_from_errno (saved_errno),
@@ -78,13 +78,13 @@ dconf_writer_create_temp_file (const gchar  *filename,
                    bytes, *tmpname, g_strerror (saved_errno));
       unlink (*tmpname); 
       g_free (*tmpname);
-      close (fd);
+      close (*fd);
 
       return NULL;
     }
 
   if ((contents = mmap (NULL, bytes, PROT_READ | PROT_WRITE,
-                        MAP_SHARED, fd, 0)) == MAP_FAILED)
+                        MAP_SHARED, *fd, 0)) == MAP_FAILED)
     {
       saved_errno = errno;
 
@@ -94,14 +94,13 @@ dconf_writer_create_temp_file (const gchar  *filename,
                    *tmpname, g_strerror (saved_errno));
       unlink (*tmpname); 
       g_free (*tmpname);
-      close (fd);
+      close (*fd);
 
       return NULL;
     }
 
   /* surely nobody's mmap is really this evil... */
   g_assert (contents != NULL);
-  close (fd);
 
   return contents;
 }
@@ -129,17 +128,13 @@ dconf_writer_rename_temp (gchar        *tmpname,
   return TRUE;
 }
 
-static guint
-dconf_writer_calculate_size (DConfWriter *writer)
-{
-  return 4;
-}
-
 gboolean
 dconf_writer_create (DConfWriter  *writer,
                      GError      **error)
 {
-  DConfWriter new_writer;
+  struct superblock *previous_super = NULL;
+  GTree *previous_contents = NULL;
+  gsize previous_size;
   gint blocks, bytes;
   gpointer contents;
   gchar *tmpname;
@@ -147,65 +142,88 @@ dconf_writer_create (DConfWriter  *writer,
   if (!dconf_writer_create_directory (writer->filename, error))
     return FALSE;
 
-  blocks = dconf_writer_calculate_size (writer);
-  bytes = blocks * sizeof (struct chunk_header) * 8;
-  bytes = (bytes + 4095) & ~4095;
+  /* if there was a database already open... */
+  if (writer->data.super != NULL)
+    {
+      /* store the information we need */
+      previous_contents = dconf_writer_flatten (writer);
+      previous_super = writer->data.super;
+      previous_size = (gchar *) writer->end - (gchar *) previous_super;
 
-  if ((contents = dconf_writer_create_temp_file (writer->filename, bytes,
-                                                 &tmpname, error)) == NULL)
-    return FALSE;
+      /* clear/reset the rest */
+      g_ptr_array_foreach (writer->extras, (GFunc) g_free, NULL);
+      g_ptr_array_set_size (writer->extras, 0);
+      writer->changed_pointer = NULL;
+      writer->changed_value = 0;
+      writer->data.super = NULL;
+      writer->end = NULL;
+      close (writer->fd);
+      writer->fd = -1;
+
+      /* treat an empty db as if there were no db at all */
+      if (g_tree_nnodes (previous_contents) == 0)
+        {
+          g_tree_unref (previous_contents);
+          previous_contents = NULL;
+        }
+      else
+        blocks = dconf_writer_measure_tree (previous_contents);
+    }
 
-  new_writer.data.super = contents;
-  new_writer.end = new_writer.data.blocks +
-                   (bytes / sizeof (struct chunk_header));
+  if (previous_contents == NULL)
+    blocks = sizeof (struct superblock) / 8;
 
-  new_writer.data.super->signature[0] = DCONF_SIGNATURE_0;
-  new_writer.data.super->signature[1] = DCONF_SIGNATURE_1;
-  new_writer.data.super->next = sizeof (struct superblock) /
-                                sizeof (struct chunk_header);
-  new_writer.data.super->root_index = 0;
+  bytes = blocks * sizeof (struct chunk_header);
+  bytes += 100;
 
-  /* the copy process should never create extras.
-     set this to NULL so that attempting to do so will crash
-   */
-  new_writer.extras = NULL;
-  new_writer.changed_pointer = NULL;
-  new_writer.changed_value = 0;
+  if ((contents = dconf_writer_create_temp_file (writer->filename,
+                                                 &writer->fd, bytes,
+                                                 &tmpname, error)) == NULL)
+    {
+      munmap (previous_super, previous_size);
+      return FALSE;
+    }
 
-  if (writer->data.super)
+  writer->data.super = contents;
+  writer->end = ((gchar *) contents) + bytes;
+  writer->data.super->signature[0] = DCONF_SIGNATURE_0;
+  writer->data.super->signature[1] = DCONF_SIGNATURE_1;
+  writer->data.super->next = sizeof (struct superblock) /
+                               sizeof (struct chunk_header);
+  writer->data.super->root_index = 0;
+
+  if (previous_contents != NULL)
     {
-      g_assert_not_reached ();
-      /* copy stuff */
+      const gchar **names;
+      GVariant **values;
+      gint num;
+
+      dconf_writer_unzip_tree (previous_contents, &names, &values, &num);
+      dconf_writer_merge (writer, "", names, values, num);
+      g_tree_unref (previous_contents);
+      g_free (values);
+      g_free (names);
+
+      g_assert (writer->extras->len == 0);
+      dconf_writer_sync (writer, NULL);
     }
 
   if (!dconf_writer_rename_temp (tmpname, writer->filename, error))
     {
+      munmap (previous_super, previous_size);
       munmap (contents, bytes);
-
       return FALSE;
     }
 
-  if (writer->data.super)
+  if (previous_super != NULL)
     {
-      gint i;
-
-      for (i = 0; i < writer->extras->len; i++)
-        g_free (g_ptr_array_index (writer->extras, i));
-
-      g_ptr_array_set_size (writer->extras, 0);
-
-      writer->data.super->flags |= DCONF_FLAG_STALE;
-      munmap (writer->data.super,
-              (gchar *) writer->end - (gchar *) writer->data.super);
+      previous_super->flags |= DCONF_FLAG_STALE;
+      munmap (previous_super, previous_size);
     }
 
-  g_assert (writer->extras->len == 0);
-  new_writer.extras = writer->extras;
-
-  g_assert (new_writer.changed_pointer == NULL);
-  g_assert (new_writer.changed_value == 0);
-
-  *writer = new_writer;
+  if G_UNLIKELY (writer->data.super->next != blocks)
+    g_critical ("New file should have been be %d blocks, but it was %d",
+                blocks, writer->data.super->next);
 
   return TRUE;
 }
@@ -216,9 +234,8 @@ dconf_writer_open (DConfWriter  *writer,
 {
   struct superblock *super;
   struct stat buf;
-  gint fd;
 
-  if ((fd = open (writer->filename, O_RDWR)) < 0)
+  if ((writer->fd = open (writer->filename, O_RDWR)) < 0)
     {
       gint saved_errno = errno;
  
@@ -230,7 +247,7 @@ dconf_writer_open (DConfWriter  *writer,
       return FALSE;
     }
 
-  if (fstat (fd, &buf))
+  if (fstat (writer->fd, &buf))
     {
       gint saved_errno = errno;
  
@@ -238,7 +255,7 @@ dconf_writer_open (DConfWriter  *writer,
                    g_file_error_from_errno (saved_errno),
                    "failed to fstat existing dconf database %s: %s",
                    writer->filename, g_strerror (saved_errno));
-      close (fd);
+      close (writer->fd);
 
       return FALSE;
     }
@@ -248,7 +265,7 @@ dconf_writer_open (DConfWriter  *writer,
       g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                    "existing dconf database file %s is too small "
                    "(%ld < 32 bytes)", writer->filename, buf.st_size);
-      close (fd);
+      close (writer->fd);
 
       return FALSE;
     }
@@ -259,14 +276,14 @@ dconf_writer_open (DConfWriter  *writer,
                    "existing dconf database file %s must be a multiple of "
                    "8 bytes in size (is %ld bytes)",
                    writer->filename, buf.st_size);
-      close (fd);
+      close (writer->fd);
 
       return FALSE;
      }
 
   if ((super = mmap (NULL, buf.st_size,
                      PROT_READ | PROT_WRITE,
-                     MAP_SHARED, fd, 0)) == MAP_FAILED)
+                     MAP_SHARED, writer->fd, 0)) == MAP_FAILED)
     {
       gint saved_errno = errno;
 
@@ -274,13 +291,11 @@ dconf_writer_open (DConfWriter  *writer,
                    g_file_error_from_errno (saved_errno),
                    "failed to memory-map existing dconf database file %s: %s",
                    writer->filename, g_strerror (saved_errno));
-      close (fd);
+      close (writer->fd);
 
       return FALSE;
     }
 
-  close (fd);
-
   if (super->signature[0] != DCONF_SIGNATURE_0 ||
       super->signature[1] != DCONF_SIGNATURE_1)
     {
@@ -341,3 +356,25 @@ dconf_writer_new (const gchar  *filename,
 
   return writer;
 }
+
+gboolean
+dconf_writer_sync (DConfWriter  *writer,
+                   GError      **error)
+{
+  if (writer->extras->len > 0)
+    return dconf_writer_create (writer, error);
+
+  fdatasync (writer->fd);
+
+  if (writer->changed_pointer != NULL)
+    {
+      *writer->changed_pointer = writer->changed_value;
+      writer->changed_pointer = NULL;
+      writer->changed_value = 0;
+
+      fdatasync (writer->fd);
+   
+   }
+
+  return TRUE;
+}
diff --git a/writer/dconf-writer-flatten.c b/writer/dconf-writer-flatten.c
index 86d2e0b..8af52bf 100644
--- a/writer/dconf-writer-flatten.c
+++ b/writer/dconf-writer-flatten.c
@@ -111,14 +111,135 @@ dconf_writer_dump_entry (gpointer key,
   return FALSE;
 }
 
+struct measure_tree_state
+{
+  const gchar *last;
+  gint blocks;
+};
+
+static gboolean
+dconf_writer_measure_entry (gpointer key,
+                            gpointer value,
+                            gpointer user_data)
+{
+  struct measure_tree_state *mts = user_data;
+  const gchar *this = key;
+  gint part = 0;
+  gint i;
+
+  /* find the common part of the path */
+  if (mts->last)
+    for (i = 0; mts->last[i] == this[i]; i++);
+  else
+    i = 0;
+
+  /* for each new directory component (ie: ending in '/'):
+   *    - we need a chunk header for that directory (1 block)
+   *    - we need a directory entry to point at that chunk (6 blocks)
+   *    - if the name is long, we need to store it separately
+   */
+  while (this[i])
+    {
+      if (this[i++] == '/')
+        {
+          mts->blocks += sizeof (struct chunk_header) / 8;
+          mts->blocks += sizeof (struct dir_entry) / 8;
+
+          if (i - part > 36)
+            {
+              /* long filename */
+              mts->blocks += sizeof (struct chunk_header) / 8;
+              mts->blocks += (i - part + 7) / 8;
+            }
+
+          part = i;
+        }
+    }
+
+  /* for each value:
+   *    - we need a chunk to store the value if it is non-atomic
+   *    - we need a directory entry in any case (6 blocks)
+   *    - if the name is long, we need to store it separately
+   */
+  if (! (/*atomic*/0))
+    {
+      mts->blocks += sizeof (struct chunk_header) / 8;
+      mts->blocks += (g_variant_get_size (value) + 7) / 8;
+    }
+
+  mts->blocks += sizeof (struct dir_entry) / 8;
+
+  if (i - part > 36)
+    {
+      /* long filename */
+      mts->blocks += sizeof (struct chunk_header) / 8;
+      mts->blocks += (i - part + 7) / 8;
+    }
+
+  /* next entry probably shares a lot in common with this one so save
+   * our path so that we can determine the amount of overlap.
+   */
+  mts->last = this;
+
+  return FALSE;
+}
+
+gint
+dconf_writer_measure_tree (GTree *tree)
+{
+  struct measure_tree_state state;
+
+  state.last = NULL;
+  state.blocks = sizeof (struct superblock) / 8;
+  state.blocks += sizeof (struct chunk_header) / 8;
+
+  g_tree_foreach (tree, dconf_writer_measure_entry, &state);
+
+  return state.blocks;
+}
+
 void
 dconf_writer_dump (DConfWriter *writer)
 {
   GTree *tree;
 
+
   tree = dconf_writer_flatten (writer);
   g_message ("dumping contents of dconf database");
   g_tree_foreach (tree, dconf_writer_dump_entry, NULL);
   g_message ("end of output");
+
+  g_message ("tree has %d blocks", dconf_writer_measure_tree (tree));
+
   g_tree_unref (tree);
 }
+
+static gboolean
+dconf_writer_unzip_entry (gpointer key,
+                          gpointer value,
+                          gpointer user_data)
+{
+  gpointer *(*args)[2] = user_data;
+
+  *((*args)[0]++) = key;
+  *((*args)[1]++) = value;
+
+  return FALSE;
+}
+
+void
+dconf_writer_unzip_tree (GTree         *tree,
+                         const gchar ***names,
+                         GVariant    ***values,
+                         gint          *num)
+{
+  gpointer args[2];
+
+  *num = g_tree_nnodes (tree);
+  *names = g_new (const gchar *, *num);
+  *values = g_new (GVariant *, *num);
+
+  args[0] = *names;
+  args[1] = *values;
+  g_tree_foreach (tree, dconf_writer_unzip_entry, args);
+}
diff --git a/writer/dconf-writer-merge.c b/writer/dconf-writer-merge.c
index b200261..41df246 100644
--- a/writer/dconf-writer-merge.c
+++ b/writer/dconf-writer-merge.c
@@ -664,13 +664,12 @@ dconf_writer_merge_index (DConfWriter  *writer,
   merge_state_assert_done (&state);
 }
 
-gboolean
+void
 dconf_writer_merge (DConfWriter  *writer,
                     const gchar  *prefix,
                     const gchar **names,
                     GVariant    **values,
-                    gint          n_items,
-                    GError      **error)
+                    gint          n_items)
 {
   volatile struct superblock *super = writer->data.super;
   guint32 index;
@@ -687,6 +686,4 @@ dconf_writer_merge (DConfWriter  *writer,
     }
   else
     g_assert (n_items == 1);
-
-  return TRUE;
 }
diff --git a/writer/dconf-writer-private.h b/writer/dconf-writer-private.h
index bbdfb60..e29b4b0 100644
--- a/writer/dconf-writer-private.h
+++ b/writer/dconf-writer-private.h
@@ -19,6 +19,7 @@
 struct OPAQUE_TYPE__DConfWriter
 {
   gchar *filename;
+  gint fd;
 
   union
   {
@@ -90,4 +91,13 @@ dconf_writer_get_entry_value (DConfWriter                     *writer,
 GTree *
 dconf_writer_flatten (DConfWriter *writer);
 
+void
+dconf_writer_unzip_tree (GTree         *tree,
+                         const gchar ***names,
+                         GVariant    ***values,
+                         gint          *num);
+
+gint
+dconf_writer_measure_tree (GTree *tree);
+
 #endif /* _dconf_writer_private_h_ */
diff --git a/writer/dconf-writer.c b/writer/dconf-writer.c
index 022ff63..503621e 100644
--- a/writer/dconf-writer.c
+++ b/writer/dconf-writer.c
@@ -239,15 +239,14 @@ dconf_writer_set_entry_name (DConfWriter               *writer,
     }
 }
 
-gboolean
+void
 dconf_writer_set (DConfWriter  *writer,
                   const gchar  *key,
-                  GVariant     *value,
-                  GError      **error)
+                  GVariant     *value)
 {
   const gchar *empty_string = "";
 
-  return dconf_writer_merge (writer, key, &empty_string, &value, 1, error);
+  return dconf_writer_merge (writer, key, &empty_string, &value, 1);
 }
 
 GVariant *
diff --git a/writer/dconf-writer.h b/writer/dconf-writer.h
index a59b54b..0d4ee66 100644
--- a/writer/dconf-writer.h
+++ b/writer/dconf-writer.h
@@ -20,10 +20,9 @@ typedef struct OPAQUE_TYPE__DConfWriter DConfWriter;
 DConfWriter *           dconf_writer_new                                (const gchar  *filename,
                                                                          GError      **error);
 
-gboolean                dconf_writer_set                                (DConfWriter  *writer,
+void                    dconf_writer_set                                (DConfWriter  *writer,
                                                                          const gchar  *key,
-                                                                         GVariant     *value,
-                                                                         GError      **error);
+                                                                         GVariant     *value);
 
 gboolean                dconf_writer_unset                              (DConfWriter  *writer,
                                                                          const gchar  *key,
@@ -34,11 +33,13 @@ gboolean                dconf_writer_set_locked                         (DConfWr
                                                                          gboolean      locked,
                                                                          GError      **error);
 
-gboolean                dconf_writer_merge                              (DConfWriter  *writer,
+void                    dconf_writer_merge                              (DConfWriter  *writer,
                                                                          const gchar  *prefix,
                                                                          const gchar **names,
                                                                          GVariant    **values,
-                                                                         gint          n_items,
+                                                                         gint          n_items);
+
+gboolean                dconf_writer_sync                               (DConfWriter  *writer,
                                                                          GError      **error);
 
 void                    dconf_writer_dump                               (DConfWriter  *writer);



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