2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2012 Canonical Limited
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the licence, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 * Author: Ryan Lortie <desrt@desrt.ca>
23 #include "dconf-gvdb-utils.h"
25 #include "../common/dconf-paths.h"
26 #include "../gvdb/gvdb-builder.h"
27 #include "../gvdb/gvdb-reader.h"
31 #include <glib/gstdio.h>
35 dconf_gvdb_utils_read_and_back_up_file (const gchar
*filename
,
36 gboolean
*file_missing
,
39 DConfChangeset
*database
;
40 GError
*my_error
= NULL
;
41 GvdbTable
*table
= NULL
;
45 if (g_file_get_contents (filename
, &contents
, &size
, &my_error
))
49 bytes
= g_bytes_new_take (contents
, size
);
50 table
= gvdb_table_new_from_bytes (bytes
, FALSE
, &my_error
);
51 g_bytes_unref (bytes
);
54 /* It is perfectly fine if the file does not exist -- then it's
57 if (g_error_matches (my_error
, G_FILE_ERROR
, G_FILE_ERROR_NOENT
))
58 g_clear_error (&my_error
);
60 /* Otherwise, we should report errors to prevent ourselves from
61 * overwriting the database in other situations...
63 if (g_error_matches (my_error
, G_FILE_ERROR
, G_FILE_ERROR_INVAL
))
65 /* Move the database to a backup file, warn and continue with a new
66 * database. The alternative is erroring out and exiting the daemon,
67 * which leaves the user’s session essentially unusable.
69 * The code to find an unused backup filename is racy, but this is an
70 * error handling path. Who cares. */
71 g_autofree gchar
*backup_filename
= NULL
;
76 (backup_filename
== NULL
|| g_file_test (backup_filename
, G_FILE_TEST_EXISTS
));
79 g_free (backup_filename
);
80 backup_filename
= g_strdup_printf ("%s~%u", filename
, i
);
83 if (g_rename (filename
, backup_filename
) != 0)
84 g_warning ("Error renaming corrupt database from ‘%s’ to ‘%s’: %s",
85 filename
, backup_filename
, g_strerror (errno
));
87 g_warning ("Database ‘%s’ was corrupt: moved it to ‘%s’ and created an empty replacement",
88 filename
, backup_filename
);
90 g_clear_error (&my_error
);
94 g_propagate_prefixed_error (error
, my_error
, "Cannot open dconf database: ");
98 /* Only allocate once we know we are in a non-error situation */
99 database
= dconf_changeset_new_database (NULL
);
101 /* Fill the table up with the initial state */
108 names
= gvdb_table_get_names (table
, &n_names
);
109 for (i
= 0; i
< n_names
; i
++)
111 if (dconf_is_key (names
[i
], NULL
))
115 value
= gvdb_table_get_value (table
, names
[i
]);
119 dconf_changeset_set (database
, names
[i
], value
);
120 g_variant_unref (value
);
127 gvdb_table_free (table
);
132 *file_missing
= (table
== NULL
);
138 dconf_gvdb_utils_get_parent (GHashTable
*table
,
141 GvdbItem
*grandparent
, *parent
;
145 if (g_str_equal (key
, "/"))
149 if (key
[len
- 1] == '/')
152 while (key
[len
- 1] != '/')
155 parent_name
= g_strndup (key
, len
);
156 parent
= g_hash_table_lookup (table
, parent_name
);
160 parent
= gvdb_hash_table_insert (table
, parent_name
);
162 grandparent
= dconf_gvdb_utils_get_parent (table
, parent_name
);
164 if (grandparent
!= NULL
)
165 gvdb_item_set_parent (parent
, grandparent
);
168 g_free (parent_name
);
174 dconf_gvdb_utils_add_key (const gchar
*path
,
178 GHashTable
*gvdb
= user_data
;
181 g_assert (g_hash_table_lookup (gvdb
, path
) == NULL
);
182 item
= gvdb_hash_table_insert (gvdb
, path
);
183 gvdb_item_set_parent (item
, dconf_gvdb_utils_get_parent (gvdb
, path
));
184 gvdb_item_set_value (item
, value
);
190 dconf_gvdb_utils_write_file (const gchar
*filename
,
191 DConfChangeset
*database
,
197 gvdb
= gvdb_hash_table_new (NULL
, NULL
);
198 dconf_changeset_all (database
, dconf_gvdb_utils_add_key
, gvdb
);
199 success
= gvdb_table_write_contents (gvdb
, filename
, FALSE
, error
);
205 /* Maybe it failed because the directory doesn't exist. Try
206 * again, after mkdir().
208 dirname
= g_path_get_dirname (filename
);
209 g_mkdir_with_parents (dirname
, 0700);
212 g_clear_error (error
);
213 success
= gvdb_table_write_contents (gvdb
, filename
, FALSE
, error
);
216 g_hash_table_unref (gvdb
);