1 | /* GConf |
---|
2 | * Copyright (C) 1999, 2000 Red Hat Inc. |
---|
3 | * |
---|
4 | * This library is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU Library General Public |
---|
6 | * License as published by the Free Software Foundation; either |
---|
7 | * version 2 of the License, or (at your option) any later version. |
---|
8 | * |
---|
9 | * This library is distributed in the hope that it will be useful, |
---|
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * Library General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU Library General Public |
---|
15 | * License along with this library; if not, write to the |
---|
16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
17 | * Boston, MA 02111-1307, USA. |
---|
18 | */ |
---|
19 | |
---|
20 | #include "xml-dir.h" |
---|
21 | #include "xml-entry.h" |
---|
22 | |
---|
23 | #include <libxml/parser.h> |
---|
24 | |
---|
25 | #include <stdio.h> |
---|
26 | #include <time.h> |
---|
27 | #include <stdlib.h> |
---|
28 | #include <string.h> |
---|
29 | #include <sys/stat.h> |
---|
30 | #include <sys/types.h> |
---|
31 | #include <fcntl.h> |
---|
32 | #include <unistd.h> |
---|
33 | #include <errno.h> |
---|
34 | #include <dirent.h> |
---|
35 | #include <limits.h> |
---|
36 | |
---|
37 | #include <gconf/gconf-internals.h> |
---|
38 | #include "xml-entry.h" |
---|
39 | |
---|
40 | /* This makes hash table safer when debugging */ |
---|
41 | #ifndef GCONF_ENABLE_DEBUG |
---|
42 | #define safe_g_hash_table_insert g_hash_table_insert |
---|
43 | #else |
---|
44 | static void |
---|
45 | safe_g_hash_table_insert(GHashTable* ht, gpointer key, gpointer value) |
---|
46 | { |
---|
47 | gpointer oldkey = NULL, oldval = NULL; |
---|
48 | |
---|
49 | if (g_hash_table_lookup_extended(ht, key, &oldkey, &oldval)) |
---|
50 | { |
---|
51 | gconf_log(GCL_WARNING, "Hash key `%s' is already in the table!", |
---|
52 | (gchar*)key); |
---|
53 | return; |
---|
54 | } |
---|
55 | else |
---|
56 | { |
---|
57 | g_hash_table_insert(ht, key, value); |
---|
58 | } |
---|
59 | } |
---|
60 | #endif |
---|
61 | |
---|
62 | static gchar* parent_dir(const gchar* dir); |
---|
63 | |
---|
64 | struct _Dir { |
---|
65 | gchar* key; |
---|
66 | gchar* fs_dirname; |
---|
67 | gchar* xml_filename; |
---|
68 | guint root_dir_len; |
---|
69 | GTime last_access; /* so we know when to un-cache */ |
---|
70 | xmlDocPtr doc; |
---|
71 | GHashTable* entry_cache; /* store key-value entries */ |
---|
72 | GHashTable* subdir_cache; /* store subdirectories */ |
---|
73 | guint dir_mode; |
---|
74 | guint file_mode; |
---|
75 | guint dirty : 1; |
---|
76 | guint deleted : 1; |
---|
77 | }; |
---|
78 | |
---|
79 | static void |
---|
80 | dir_load_doc(Dir* d, GError** err); |
---|
81 | |
---|
82 | static Entry* dir_make_new_entry(Dir* d, const gchar* relative_key); |
---|
83 | |
---|
84 | static gboolean dir_forget_entry_if_useless(Dir* d, Entry* e); |
---|
85 | |
---|
86 | static Dir* |
---|
87 | dir_blank(const gchar* key) |
---|
88 | { |
---|
89 | Dir* d; |
---|
90 | |
---|
91 | d = g_new0(Dir, 1); |
---|
92 | |
---|
93 | #ifdef GCONF_ENABLE_DEBUG |
---|
94 | { |
---|
95 | gchar* why; |
---|
96 | if (!gconf_valid_key(key, &why)) { |
---|
97 | gconf_log(GCL_DEBUG, "key `%s' invalid: %s", |
---|
98 | key, why); |
---|
99 | } |
---|
100 | g_assert(gconf_valid_key(key, NULL)); |
---|
101 | } |
---|
102 | #endif |
---|
103 | |
---|
104 | d->key = g_strdup(key); |
---|
105 | |
---|
106 | d->last_access = time(NULL); |
---|
107 | d->doc = NULL; |
---|
108 | |
---|
109 | d->entry_cache = g_hash_table_new(g_str_hash, g_str_equal); |
---|
110 | |
---|
111 | d->dirty = FALSE; |
---|
112 | d->deleted = FALSE; |
---|
113 | |
---|
114 | d->dir_mode = 0700; |
---|
115 | d->file_mode = 0600; |
---|
116 | |
---|
117 | return d; |
---|
118 | } |
---|
119 | |
---|
120 | Dir* |
---|
121 | dir_new(const gchar *keyname, |
---|
122 | const gchar *xml_root_dir, |
---|
123 | guint dir_mode, |
---|
124 | guint file_mode) |
---|
125 | { |
---|
126 | Dir* d; |
---|
127 | |
---|
128 | d = dir_blank(keyname); |
---|
129 | |
---|
130 | /* sync with dir_load() */ |
---|
131 | d->fs_dirname = gconf_concat_dir_and_key(xml_root_dir, keyname); |
---|
132 | d->xml_filename = g_strconcat(d->fs_dirname, "/%gconf.xml", NULL); |
---|
133 | d->root_dir_len = strlen(xml_root_dir); |
---|
134 | |
---|
135 | d->dir_mode = dir_mode; |
---|
136 | d->file_mode = file_mode; |
---|
137 | |
---|
138 | return d; |
---|
139 | } |
---|
140 | |
---|
141 | Dir* |
---|
142 | dir_load (const gchar* key, const gchar* xml_root_dir, GError** err) |
---|
143 | { |
---|
144 | Dir* d; |
---|
145 | gchar* fs_dirname; |
---|
146 | gchar* xml_filename; |
---|
147 | guint dir_mode = 0700; |
---|
148 | guint file_mode = 0600; |
---|
149 | |
---|
150 | g_return_val_if_fail(gconf_valid_key(key, NULL), NULL); |
---|
151 | |
---|
152 | fs_dirname = gconf_concat_dir_and_key(xml_root_dir, key); |
---|
153 | xml_filename = g_strconcat(fs_dirname, "/%gconf.xml", NULL); |
---|
154 | |
---|
155 | { |
---|
156 | struct stat s; |
---|
157 | gboolean notfound = FALSE; |
---|
158 | |
---|
159 | if (stat(xml_filename, &s) != 0) |
---|
160 | { |
---|
161 | if (errno != ENOENT) |
---|
162 | { |
---|
163 | gconf_set_error(err, GCONF_ERROR_FAILED, |
---|
164 | _("Could not stat `%s': %s"), |
---|
165 | xml_filename, strerror(errno)); |
---|
166 | |
---|
167 | } |
---|
168 | |
---|
169 | notfound = TRUE; |
---|
170 | } |
---|
171 | else if (S_ISDIR(s.st_mode)) |
---|
172 | { |
---|
173 | gconf_set_error(err, GCONF_ERROR_FAILED, |
---|
174 | _("XML filename `%s' is a directory"), |
---|
175 | xml_filename); |
---|
176 | notfound = TRUE; |
---|
177 | } |
---|
178 | |
---|
179 | if (notfound) |
---|
180 | { |
---|
181 | gconf_log(GCL_DEBUG, "dir file %s not found", xml_filename); |
---|
182 | g_free(fs_dirname); |
---|
183 | g_free(xml_filename); |
---|
184 | return NULL; |
---|
185 | } |
---|
186 | else |
---|
187 | { |
---|
188 | /* Take directory mode from the xml_root_dir, if possible */ |
---|
189 | if (stat (xml_root_dir, &s) == 0) |
---|
190 | { |
---|
191 | dir_mode = mode_t_to_mode(s.st_mode); |
---|
192 | } |
---|
193 | |
---|
194 | file_mode = dir_mode & ~0111; /* turn off search bits */ |
---|
195 | } |
---|
196 | } |
---|
197 | |
---|
198 | d = dir_blank(key); |
---|
199 | |
---|
200 | /* sync with dir_new() */ |
---|
201 | d->fs_dirname = fs_dirname; |
---|
202 | d->xml_filename = xml_filename; |
---|
203 | d->root_dir_len = strlen(xml_root_dir); |
---|
204 | |
---|
205 | d->dir_mode = dir_mode; |
---|
206 | d->file_mode = file_mode; |
---|
207 | |
---|
208 | gconf_log(GCL_DEBUG, "loaded dir %s", fs_dirname); |
---|
209 | |
---|
210 | return d; |
---|
211 | } |
---|
212 | |
---|
213 | |
---|
214 | static void |
---|
215 | entry_destroy_foreach(const gchar* name, Entry* e, gpointer data) |
---|
216 | { |
---|
217 | entry_destroy(e); |
---|
218 | } |
---|
219 | |
---|
220 | void |
---|
221 | dir_destroy(Dir* d) |
---|
222 | { |
---|
223 | g_free(d->key); |
---|
224 | g_free(d->fs_dirname); |
---|
225 | g_free(d->xml_filename); |
---|
226 | |
---|
227 | g_hash_table_foreach(d->entry_cache, (GHFunc)entry_destroy_foreach, |
---|
228 | NULL); |
---|
229 | |
---|
230 | g_hash_table_destroy(d->entry_cache); |
---|
231 | |
---|
232 | if (d->doc != NULL) |
---|
233 | xmlFreeDoc(d->doc); |
---|
234 | |
---|
235 | g_free(d); |
---|
236 | } |
---|
237 | |
---|
238 | static gboolean |
---|
239 | create_fs_dir(const gchar* dir, const gchar* xml_filename, |
---|
240 | guint root_dir_len, |
---|
241 | guint dir_mode, guint file_mode, |
---|
242 | GError** err); |
---|
243 | |
---|
244 | gboolean |
---|
245 | dir_ensure_exists (Dir* d, |
---|
246 | GError** err) |
---|
247 | { |
---|
248 | if (!create_fs_dir(d->fs_dirname, d->xml_filename, d->root_dir_len, |
---|
249 | d->dir_mode, d->file_mode, |
---|
250 | err)) |
---|
251 | { |
---|
252 | |
---|
253 | /* check that error is set */ |
---|
254 | g_return_val_if_fail( (err == NULL) || (*err != NULL), FALSE ); |
---|
255 | |
---|
256 | return FALSE; |
---|
257 | } |
---|
258 | else |
---|
259 | { |
---|
260 | return TRUE; |
---|
261 | } |
---|
262 | } |
---|
263 | |
---|
264 | static void |
---|
265 | entry_sync_foreach(const gchar* name, Entry* e, gpointer data) |
---|
266 | { |
---|
267 | entry_sync_to_node(e); |
---|
268 | } |
---|
269 | |
---|
270 | gboolean |
---|
271 | dir_sync_pending (Dir *d) |
---|
272 | { |
---|
273 | return d->dirty; |
---|
274 | } |
---|
275 | |
---|
276 | gboolean |
---|
277 | dir_sync (Dir* d, GError** err) |
---|
278 | { |
---|
279 | gboolean retval = TRUE; |
---|
280 | |
---|
281 | /* note that if we are deleted but already |
---|
282 | synced, this returns now, making the |
---|
283 | dircache's recursive delete tactic reasonably |
---|
284 | efficient |
---|
285 | */ |
---|
286 | if (!d->dirty) |
---|
287 | return TRUE; |
---|
288 | |
---|
289 | /* We should have a doc if dirty is TRUE */ |
---|
290 | g_assert(d->doc != NULL); |
---|
291 | |
---|
292 | d->last_access = time(NULL); |
---|
293 | |
---|
294 | if (d->deleted) |
---|
295 | { |
---|
296 | if (unlink(d->xml_filename) != 0) |
---|
297 | { |
---|
298 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to delete `%s': %s"), |
---|
299 | d->xml_filename, strerror(errno)); |
---|
300 | return FALSE; |
---|
301 | } |
---|
302 | |
---|
303 | if (rmdir(d->fs_dirname) != 0) |
---|
304 | { |
---|
305 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to delete `%s': %s"), |
---|
306 | d->fs_dirname, strerror(errno)); |
---|
307 | return FALSE; |
---|
308 | } |
---|
309 | } |
---|
310 | else |
---|
311 | { |
---|
312 | gboolean old_existed = FALSE; |
---|
313 | gchar* tmp_filename; |
---|
314 | gchar* old_filename; |
---|
315 | |
---|
316 | /* First make sure entry values are synced to their |
---|
317 | XML nodes */ |
---|
318 | g_hash_table_foreach(d->entry_cache, (GHFunc)entry_sync_foreach, NULL); |
---|
319 | |
---|
320 | tmp_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.tmp", NULL); |
---|
321 | old_filename = g_strconcat(d->fs_dirname, "/%gconf.xml.old", NULL); |
---|
322 | |
---|
323 | if (xmlSaveFile(tmp_filename, d->doc) < 0) |
---|
324 | { |
---|
325 | gboolean recovered = FALSE; |
---|
326 | |
---|
327 | /* Try to solve the problem by creating the FS dir */ |
---|
328 | if (!gconf_file_exists(d->fs_dirname)) |
---|
329 | { |
---|
330 | if (create_fs_dir(d->fs_dirname, d->xml_filename, |
---|
331 | d->root_dir_len, |
---|
332 | d->dir_mode, d->file_mode, |
---|
333 | err)) |
---|
334 | { |
---|
335 | if (xmlSaveFile(tmp_filename, d->doc) >= 0) |
---|
336 | recovered = TRUE; |
---|
337 | } |
---|
338 | } |
---|
339 | |
---|
340 | if (!recovered) |
---|
341 | { |
---|
342 | /* I think libxml may mangle errno, but we might as well |
---|
343 | try. Don't set error if it's already set by some |
---|
344 | earlier failure. */ |
---|
345 | if (err && *err == NULL) |
---|
346 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to write file `%s': %s"), |
---|
347 | tmp_filename, strerror(errno)); |
---|
348 | |
---|
349 | retval = FALSE; |
---|
350 | |
---|
351 | goto failed_end_of_sync; |
---|
352 | } |
---|
353 | } |
---|
354 | |
---|
355 | /* Set permissions on the new file */ |
---|
356 | if (chmod (tmp_filename, d->file_mode) != 0) |
---|
357 | { |
---|
358 | gconf_set_error(err, GCONF_ERROR_FAILED, |
---|
359 | _("Failed to set mode on `%s': %s"), |
---|
360 | tmp_filename, strerror(errno)); |
---|
361 | |
---|
362 | retval = FALSE; |
---|
363 | goto failed_end_of_sync; |
---|
364 | } |
---|
365 | |
---|
366 | old_existed = gconf_file_exists(d->xml_filename); |
---|
367 | |
---|
368 | if (old_existed) |
---|
369 | { |
---|
370 | if (rename(d->xml_filename, old_filename) < 0) |
---|
371 | { |
---|
372 | gconf_set_error(err, GCONF_ERROR_FAILED, |
---|
373 | _("Failed to rename `%s' to `%s': %s"), |
---|
374 | d->xml_filename, old_filename, strerror(errno)); |
---|
375 | |
---|
376 | retval = FALSE; |
---|
377 | goto failed_end_of_sync; |
---|
378 | } |
---|
379 | } |
---|
380 | |
---|
381 | if (rename(tmp_filename, d->xml_filename) < 0) |
---|
382 | { |
---|
383 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to rename `%s' to `%s': %s"), |
---|
384 | tmp_filename, d->xml_filename, strerror(errno)); |
---|
385 | |
---|
386 | /* Put the original file back, so this isn't a total disaster. */ |
---|
387 | if (rename(old_filename, d->xml_filename) < 0) |
---|
388 | { |
---|
389 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to restore `%s' from `%s': %s"), |
---|
390 | d->xml_filename, old_filename, strerror(errno)); |
---|
391 | } |
---|
392 | |
---|
393 | retval = FALSE; |
---|
394 | goto failed_end_of_sync; |
---|
395 | } |
---|
396 | |
---|
397 | if (old_existed) |
---|
398 | { |
---|
399 | if (unlink(old_filename) < 0) |
---|
400 | { |
---|
401 | gconf_log(GCL_WARNING, _("Failed to delete old file `%s': %s"), |
---|
402 | old_filename, strerror(errno)); |
---|
403 | /* Not a failure, just leaves cruft around. */ |
---|
404 | } |
---|
405 | } |
---|
406 | |
---|
407 | failed_end_of_sync: |
---|
408 | |
---|
409 | g_free(old_filename); |
---|
410 | g_free(tmp_filename); |
---|
411 | } |
---|
412 | |
---|
413 | if (retval) |
---|
414 | d->dirty = FALSE; |
---|
415 | |
---|
416 | return retval; |
---|
417 | } |
---|
418 | |
---|
419 | void |
---|
420 | dir_set_value (Dir* d, const gchar* relative_key, |
---|
421 | GConfValue* value, GError** err) |
---|
422 | { |
---|
423 | Entry* e; |
---|
424 | |
---|
425 | if (d->doc == NULL) |
---|
426 | dir_load_doc(d, err); |
---|
427 | |
---|
428 | if (d->doc == NULL) |
---|
429 | { |
---|
430 | g_return_if_fail( (err == NULL) || (*err != NULL) ); |
---|
431 | return; |
---|
432 | } |
---|
433 | |
---|
434 | e = g_hash_table_lookup(d->entry_cache, relative_key); |
---|
435 | |
---|
436 | if (e == NULL) |
---|
437 | e = dir_make_new_entry(d, relative_key); |
---|
438 | |
---|
439 | entry_set_value(e, value); |
---|
440 | |
---|
441 | d->last_access = time(NULL); |
---|
442 | entry_set_mod_time(e, d->last_access); |
---|
443 | |
---|
444 | entry_set_mod_user(e, g_get_user_name()); |
---|
445 | |
---|
446 | d->dirty = TRUE; |
---|
447 | } |
---|
448 | |
---|
449 | GTime |
---|
450 | dir_get_last_access (Dir *d) |
---|
451 | { |
---|
452 | return d->last_access; |
---|
453 | } |
---|
454 | |
---|
455 | GConfValue* |
---|
456 | dir_get_value (Dir* d, |
---|
457 | const gchar* relative_key, |
---|
458 | const gchar** locales, |
---|
459 | gchar** schema_name, |
---|
460 | GError** err) |
---|
461 | { |
---|
462 | Entry* e; |
---|
463 | |
---|
464 | if (d->doc == NULL) |
---|
465 | dir_load_doc(d, err); |
---|
466 | |
---|
467 | if (d->doc == NULL) |
---|
468 | { |
---|
469 | g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL ); |
---|
470 | return NULL; |
---|
471 | } |
---|
472 | |
---|
473 | e = g_hash_table_lookup(d->entry_cache, relative_key); |
---|
474 | |
---|
475 | d->last_access = time(NULL); |
---|
476 | |
---|
477 | if (e == NULL) |
---|
478 | { |
---|
479 | /* No entry; return */ |
---|
480 | return NULL; |
---|
481 | } |
---|
482 | else |
---|
483 | { |
---|
484 | GConfValue* val; |
---|
485 | |
---|
486 | g_assert(e != NULL); |
---|
487 | |
---|
488 | val = entry_get_value (e, locales, err); |
---|
489 | |
---|
490 | /* Get schema name if requested */ |
---|
491 | if (schema_name && entry_get_schema_name (e)) |
---|
492 | *schema_name = g_strdup (entry_get_schema_name (e)); |
---|
493 | |
---|
494 | /* return copy of the value */ |
---|
495 | if (val != NULL) |
---|
496 | return gconf_value_copy(val); |
---|
497 | else |
---|
498 | return NULL; |
---|
499 | } |
---|
500 | } |
---|
501 | |
---|
502 | const gchar* |
---|
503 | dir_get_name (Dir *d) |
---|
504 | { |
---|
505 | g_return_val_if_fail(d != NULL, NULL); |
---|
506 | return d->key; |
---|
507 | } |
---|
508 | |
---|
509 | GConfMetaInfo* |
---|
510 | dir_get_metainfo(Dir* d, const gchar* relative_key, GError** err) |
---|
511 | { |
---|
512 | Entry* e; |
---|
513 | |
---|
514 | d->last_access = time(NULL); |
---|
515 | |
---|
516 | if (d->doc == NULL) |
---|
517 | dir_load_doc(d, err); |
---|
518 | |
---|
519 | if (d->doc == NULL) |
---|
520 | { |
---|
521 | g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL ); |
---|
522 | return NULL; |
---|
523 | } |
---|
524 | |
---|
525 | e = g_hash_table_lookup(d->entry_cache, relative_key); |
---|
526 | |
---|
527 | if (e == NULL) |
---|
528 | return NULL; |
---|
529 | else |
---|
530 | return entry_get_metainfo(e); |
---|
531 | } |
---|
532 | |
---|
533 | void |
---|
534 | dir_unset_value (Dir* d, const gchar* relative_key, |
---|
535 | const gchar* locale, GError** err) |
---|
536 | { |
---|
537 | Entry* e; |
---|
538 | |
---|
539 | d->last_access = time(NULL); |
---|
540 | |
---|
541 | if (d->doc == NULL) |
---|
542 | dir_load_doc(d, err); |
---|
543 | |
---|
544 | if (d->doc == NULL) |
---|
545 | { |
---|
546 | g_return_if_fail( (err == NULL) || (*err != NULL) ); |
---|
547 | return; |
---|
548 | } |
---|
549 | |
---|
550 | e = g_hash_table_lookup(d->entry_cache, relative_key); |
---|
551 | |
---|
552 | if (e == NULL) /* nothing to change */ |
---|
553 | return; |
---|
554 | |
---|
555 | if (entry_unset_value(e, locale)) |
---|
556 | { |
---|
557 | /* If entry_unset() returns TRUE then |
---|
558 | the entry was changed (not already unset) */ |
---|
559 | |
---|
560 | d->dirty = TRUE; |
---|
561 | |
---|
562 | if (dir_forget_entry_if_useless(d, e)) |
---|
563 | { |
---|
564 | /* entry is destroyed */ |
---|
565 | return; |
---|
566 | } |
---|
567 | else |
---|
568 | { |
---|
569 | entry_set_mod_time(e, d->last_access); |
---|
570 | entry_set_mod_user(e, g_get_user_name()); |
---|
571 | } |
---|
572 | } |
---|
573 | else |
---|
574 | { |
---|
575 | /* Check uselessness anyway; this ensures that if it was useless |
---|
576 | when the daemon started or we otherwise missed its lack of |
---|
577 | utility, we clean it up if the user does an explicit unset */ |
---|
578 | dir_forget_entry_if_useless(d, e); |
---|
579 | } |
---|
580 | } |
---|
581 | |
---|
582 | typedef struct _ListifyData ListifyData; |
---|
583 | |
---|
584 | struct _ListifyData { |
---|
585 | GSList* list; |
---|
586 | const gchar* name; |
---|
587 | const gchar** locales; |
---|
588 | }; |
---|
589 | |
---|
590 | static void |
---|
591 | listify_foreach(const gchar* key, Entry* e, ListifyData* ld) |
---|
592 | { |
---|
593 | GConfValue* val; |
---|
594 | GConfEntry* entry; |
---|
595 | GError* error = NULL; |
---|
596 | |
---|
597 | val = entry_get_value (e, ld->locales, &error); |
---|
598 | |
---|
599 | if (error != NULL) |
---|
600 | { |
---|
601 | g_assert (val == NULL); |
---|
602 | g_error_free (error); |
---|
603 | return; |
---|
604 | } |
---|
605 | |
---|
606 | entry = gconf_entry_new_nocopy (g_strdup(key), |
---|
607 | val ? gconf_value_copy(val) : NULL); |
---|
608 | |
---|
609 | if (val == NULL && |
---|
610 | entry_get_schema_name (e)) |
---|
611 | { |
---|
612 | gconf_entry_set_schema_name (entry, entry_get_schema_name (e)); |
---|
613 | } |
---|
614 | |
---|
615 | ld->list = g_slist_prepend(ld->list, entry); |
---|
616 | } |
---|
617 | |
---|
618 | GSList* |
---|
619 | dir_all_entries (Dir* d, const gchar** locales, GError** err) |
---|
620 | { |
---|
621 | ListifyData ld; |
---|
622 | |
---|
623 | if (d->doc == NULL) |
---|
624 | dir_load_doc(d, err); |
---|
625 | |
---|
626 | if (d->doc == NULL) |
---|
627 | { |
---|
628 | g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL ); |
---|
629 | return NULL; |
---|
630 | } |
---|
631 | |
---|
632 | ld.list = NULL; |
---|
633 | ld.name = d->key; |
---|
634 | ld.locales = locales; |
---|
635 | |
---|
636 | g_hash_table_foreach(d->entry_cache, (GHFunc)listify_foreach, |
---|
637 | &ld); |
---|
638 | |
---|
639 | return ld.list; |
---|
640 | } |
---|
641 | |
---|
642 | GSList* |
---|
643 | dir_all_subdirs (Dir* d, GError** err) |
---|
644 | { |
---|
645 | DIR* dp; |
---|
646 | struct dirent* dent; |
---|
647 | struct stat statbuf; |
---|
648 | GSList* retval = NULL; |
---|
649 | gchar* fullpath; |
---|
650 | gchar* fullpath_end; |
---|
651 | guint len; |
---|
652 | guint subdir_len; |
---|
653 | |
---|
654 | if (d->doc == NULL) |
---|
655 | dir_load_doc(d, err); |
---|
656 | |
---|
657 | if (d->doc == NULL) |
---|
658 | { |
---|
659 | g_return_val_if_fail( (err == NULL) || (*err != NULL), NULL ); |
---|
660 | return NULL; |
---|
661 | } |
---|
662 | |
---|
663 | dp = opendir(d->fs_dirname); |
---|
664 | |
---|
665 | if (dp == NULL) |
---|
666 | return NULL; |
---|
667 | |
---|
668 | len = strlen(d->fs_dirname); |
---|
669 | subdir_len = PATH_MAX - len; |
---|
670 | |
---|
671 | fullpath = g_malloc0(subdir_len + len + 20); /* ensure null termination */ |
---|
672 | strcpy(fullpath, d->fs_dirname); |
---|
673 | |
---|
674 | fullpath_end = fullpath + len; |
---|
675 | *fullpath_end = '/'; |
---|
676 | ++fullpath_end; |
---|
677 | *fullpath_end = '\0'; |
---|
678 | |
---|
679 | while ((dent = readdir(dp)) != NULL) |
---|
680 | { |
---|
681 | /* ignore ., .., and all dot-files */ |
---|
682 | if (dent->d_name[0] == '.') |
---|
683 | continue; |
---|
684 | |
---|
685 | len = strlen(dent->d_name); |
---|
686 | |
---|
687 | if (len < subdir_len) |
---|
688 | { |
---|
689 | strcpy(fullpath_end, dent->d_name); |
---|
690 | strncpy(fullpath_end+len, "/%gconf.xml", subdir_len - len); |
---|
691 | } |
---|
692 | else |
---|
693 | continue; /* Shouldn't ever happen since PATH_MAX is available */ |
---|
694 | |
---|
695 | if (stat(fullpath, &statbuf) < 0) |
---|
696 | { |
---|
697 | /* This is some kind of cruft, not an XML directory */ |
---|
698 | continue; |
---|
699 | } |
---|
700 | |
---|
701 | retval = g_slist_prepend(retval, g_strdup(dent->d_name)); |
---|
702 | } |
---|
703 | |
---|
704 | /* if this fails, we really can't do a thing about it |
---|
705 | and it's not a meaningful error */ |
---|
706 | closedir(dp); |
---|
707 | |
---|
708 | g_free (fullpath); |
---|
709 | |
---|
710 | return retval; |
---|
711 | } |
---|
712 | |
---|
713 | void |
---|
714 | dir_set_schema (Dir* d, |
---|
715 | const gchar* relative_key, |
---|
716 | const gchar* schema_key, |
---|
717 | GError** err) |
---|
718 | { |
---|
719 | Entry* e; |
---|
720 | |
---|
721 | if (d->doc == NULL) |
---|
722 | dir_load_doc(d, err); |
---|
723 | |
---|
724 | if (d->doc == NULL) |
---|
725 | { |
---|
726 | g_return_if_fail( (err == NULL) || (*err != NULL) ); |
---|
727 | return; |
---|
728 | } |
---|
729 | |
---|
730 | d->dirty = TRUE; |
---|
731 | d->last_access = time(NULL); |
---|
732 | |
---|
733 | e = g_hash_table_lookup(d->entry_cache, relative_key); |
---|
734 | |
---|
735 | if (e == NULL) |
---|
736 | e = dir_make_new_entry(d, relative_key); |
---|
737 | |
---|
738 | entry_set_mod_time(e, d->last_access); |
---|
739 | |
---|
740 | entry_set_schema_name(e, schema_key); |
---|
741 | |
---|
742 | if (schema_key == NULL) |
---|
743 | dir_forget_entry_if_useless(d, e); |
---|
744 | } |
---|
745 | |
---|
746 | void |
---|
747 | dir_mark_deleted(Dir* d) |
---|
748 | { |
---|
749 | if (d->deleted) |
---|
750 | return; |
---|
751 | |
---|
752 | d->deleted = TRUE; |
---|
753 | d->dirty = TRUE; |
---|
754 | |
---|
755 | /* go ahead and free the XML document */ |
---|
756 | |
---|
757 | if (d->doc) |
---|
758 | xmlFreeDoc(d->doc); |
---|
759 | d->doc = NULL; |
---|
760 | } |
---|
761 | |
---|
762 | gboolean |
---|
763 | dir_is_deleted (Dir* d) |
---|
764 | { |
---|
765 | return d->deleted; |
---|
766 | } |
---|
767 | |
---|
768 | GTime |
---|
769 | dir_last_access (Dir* d) |
---|
770 | { |
---|
771 | return d->last_access; |
---|
772 | } |
---|
773 | |
---|
774 | /* private Dir functions */ |
---|
775 | |
---|
776 | static void |
---|
777 | dir_fill_cache_from_doc(Dir* d); |
---|
778 | |
---|
779 | static void |
---|
780 | dir_load_doc(Dir* d, GError** err) |
---|
781 | { |
---|
782 | gboolean xml_already_exists = TRUE; |
---|
783 | gboolean need_backup = FALSE; |
---|
784 | struct stat statbuf; |
---|
785 | |
---|
786 | g_return_if_fail(d->doc == NULL); |
---|
787 | |
---|
788 | if (stat(d->xml_filename, &statbuf) < 0) |
---|
789 | { |
---|
790 | switch (errno) |
---|
791 | { |
---|
792 | case ENOENT: |
---|
793 | xml_already_exists = FALSE; |
---|
794 | break; |
---|
795 | case ENOTDIR: |
---|
796 | case ELOOP: |
---|
797 | case EFAULT: |
---|
798 | case EACCES: |
---|
799 | case ENOMEM: |
---|
800 | case ENAMETOOLONG: |
---|
801 | default: |
---|
802 | /* These are all fatal errors */ |
---|
803 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to stat `%s': %s"), |
---|
804 | d->xml_filename, strerror(errno)); |
---|
805 | return; |
---|
806 | break; |
---|
807 | } |
---|
808 | } |
---|
809 | |
---|
810 | if (statbuf.st_size == 0) |
---|
811 | { |
---|
812 | xml_already_exists = FALSE; |
---|
813 | } |
---|
814 | |
---|
815 | if (xml_already_exists) |
---|
816 | d->doc = xmlParseFile(d->xml_filename); |
---|
817 | |
---|
818 | /* We recover from these errors instead of passing them up */ |
---|
819 | |
---|
820 | /* This has the potential to just blow away an entire corrupted |
---|
821 | config file; but I think that is better than the alternatives |
---|
822 | (disabling config for a directory because the document is mangled) |
---|
823 | */ |
---|
824 | |
---|
825 | /* Also we create empty %gconf.xml files when we create a new dir, |
---|
826 | and those return a parse error */ |
---|
827 | |
---|
828 | if (d->doc == NULL) |
---|
829 | { |
---|
830 | if (xml_already_exists) |
---|
831 | need_backup = TRUE; /* we want to save whatever broken stuff was in the file */ |
---|
832 | |
---|
833 | /* Create a new doc */ |
---|
834 | |
---|
835 | d->doc = xmlNewDoc("1.0"); |
---|
836 | } |
---|
837 | |
---|
838 | if (d->doc->root == NULL) |
---|
839 | { |
---|
840 | /* fill it in */ |
---|
841 | d->doc->root = xmlNewDocNode(d->doc, NULL, "gconf", NULL); |
---|
842 | } |
---|
843 | else if (strcmp(d->doc->root->name, "gconf") != 0) |
---|
844 | { |
---|
845 | xmlFreeDoc(d->doc); |
---|
846 | d->doc = xmlNewDoc("1.0"); |
---|
847 | d->doc->root = xmlNewDocNode(d->doc, NULL, "gconf", NULL); |
---|
848 | need_backup = TRUE; /* save broken stuff */ |
---|
849 | } |
---|
850 | else |
---|
851 | { |
---|
852 | /* We had an initial doc with a valid root */ |
---|
853 | /* Fill child_cache from entries */ |
---|
854 | dir_fill_cache_from_doc(d); |
---|
855 | } |
---|
856 | |
---|
857 | if (need_backup) |
---|
858 | { |
---|
859 | /* Back up the file we failed to parse, if it exists, |
---|
860 | we aren't going to be able to do anything if this call |
---|
861 | fails |
---|
862 | */ |
---|
863 | |
---|
864 | gchar* backup = g_strconcat(d->xml_filename, ".bak", NULL); |
---|
865 | int fd; |
---|
866 | |
---|
867 | rename(d->xml_filename, backup); |
---|
868 | |
---|
869 | /* Recreate %gconf.xml to maintain our integrity and be sure |
---|
870 | all_subdirs works */ |
---|
871 | /* If we failed to rename, we just give up and truncate the file */ |
---|
872 | fd = open(d->xml_filename, O_CREAT | O_WRONLY | O_TRUNC, d->file_mode); |
---|
873 | if (fd >= 0) |
---|
874 | close(fd); |
---|
875 | |
---|
876 | g_free(backup); |
---|
877 | } |
---|
878 | |
---|
879 | g_assert(d->doc != NULL); |
---|
880 | g_assert(d->doc->root != NULL); |
---|
881 | } |
---|
882 | |
---|
883 | static Entry* |
---|
884 | dir_make_new_entry(Dir* d, const gchar* relative_key) |
---|
885 | { |
---|
886 | Entry* e; |
---|
887 | |
---|
888 | g_return_val_if_fail(d->doc != NULL, NULL); |
---|
889 | g_return_val_if_fail(d->doc->root != NULL, NULL); |
---|
890 | |
---|
891 | e = entry_new(relative_key); |
---|
892 | |
---|
893 | entry_set_node(e, xmlNewChild(d->doc->root, NULL, "entry", NULL)); |
---|
894 | |
---|
895 | safe_g_hash_table_insert(d->entry_cache, (gchar*)entry_get_name(e), e); |
---|
896 | |
---|
897 | return e; |
---|
898 | } |
---|
899 | |
---|
900 | static gboolean |
---|
901 | dir_forget_entry_if_useless(Dir* d, Entry* e) |
---|
902 | { |
---|
903 | GConfValue* val; |
---|
904 | |
---|
905 | if (entry_get_schema_name(e) != NULL) |
---|
906 | return FALSE; |
---|
907 | |
---|
908 | val = entry_get_value(e, NULL, NULL); |
---|
909 | |
---|
910 | if (val != NULL) |
---|
911 | return FALSE; /* not useless */ |
---|
912 | |
---|
913 | g_hash_table_remove(d->entry_cache, entry_get_name(e)); |
---|
914 | |
---|
915 | entry_destroy(e); |
---|
916 | |
---|
917 | return TRUE; |
---|
918 | } |
---|
919 | |
---|
920 | static void |
---|
921 | dir_fill_cache_from_doc(Dir* d) |
---|
922 | { |
---|
923 | xmlNodePtr node; |
---|
924 | |
---|
925 | if (d->doc == NULL || |
---|
926 | d->doc->root == NULL || |
---|
927 | d->doc->root->childs == NULL) |
---|
928 | { |
---|
929 | /* Empty document - just return. */ |
---|
930 | return; |
---|
931 | } |
---|
932 | |
---|
933 | node = d->doc->root->childs; |
---|
934 | |
---|
935 | while (node != NULL) |
---|
936 | { |
---|
937 | if (node->type == XML_ELEMENT_NODE && |
---|
938 | (strcmp(node->name, "entry") == 0)) |
---|
939 | { |
---|
940 | gchar* attr = my_xmlGetProp(node, "name"); |
---|
941 | |
---|
942 | if (attr != NULL) |
---|
943 | { |
---|
944 | if (g_hash_table_lookup(d->entry_cache, attr) != NULL) |
---|
945 | { |
---|
946 | gconf_log(GCL_WARNING, |
---|
947 | _("Duplicate entry `%s' in `%s', ignoring"), |
---|
948 | attr, d->xml_filename); |
---|
949 | } |
---|
950 | else |
---|
951 | { |
---|
952 | Entry* e; |
---|
953 | |
---|
954 | e = entry_new(attr); |
---|
955 | |
---|
956 | entry_set_node(e, node); |
---|
957 | |
---|
958 | entry_fill_from_node(e); |
---|
959 | |
---|
960 | safe_g_hash_table_insert(d->entry_cache, |
---|
961 | (gchar*)entry_get_name(e), e); |
---|
962 | } |
---|
963 | |
---|
964 | free(attr); |
---|
965 | } |
---|
966 | else |
---|
967 | { |
---|
968 | gconf_log(GCL_WARNING, |
---|
969 | _("Entry with no name in XML file `%s', ignoring"), |
---|
970 | d->xml_filename); |
---|
971 | } |
---|
972 | } |
---|
973 | else |
---|
974 | { |
---|
975 | if (node->type == XML_ELEMENT_NODE) |
---|
976 | gconf_log(GCL_WARNING, |
---|
977 | _("A toplevel node in XML file `%s' is <%s> rather than <entry>, ignoring"), |
---|
978 | d->xml_filename, |
---|
979 | node->name ? (char*) node->name : "unknown"); |
---|
980 | } |
---|
981 | |
---|
982 | node = node->next; |
---|
983 | } |
---|
984 | } |
---|
985 | |
---|
986 | /* |
---|
987 | * Misc |
---|
988 | */ |
---|
989 | |
---|
990 | static gboolean |
---|
991 | create_fs_dir(const gchar* dir, const gchar* xml_filename, |
---|
992 | guint root_dir_len, guint dir_mode, guint file_mode, |
---|
993 | GError** err) |
---|
994 | { |
---|
995 | g_return_val_if_fail(xml_filename != NULL, FALSE); |
---|
996 | |
---|
997 | gconf_log(GCL_DEBUG, "Enter create_fs_dir: %s", dir); |
---|
998 | |
---|
999 | if (gconf_file_test(xml_filename, GCONF_FILE_ISFILE)) |
---|
1000 | { |
---|
1001 | gconf_log(GCL_DEBUG, "XML backend file %s already exists", xml_filename); |
---|
1002 | return TRUE; |
---|
1003 | } |
---|
1004 | |
---|
1005 | /* Don't create anything above the root directory */ |
---|
1006 | if (strlen(dir) > root_dir_len) |
---|
1007 | { |
---|
1008 | gchar* parent; |
---|
1009 | |
---|
1010 | parent = parent_dir(dir); |
---|
1011 | |
---|
1012 | gconf_log(GCL_DEBUG, "Parent dir is %s", parent); |
---|
1013 | |
---|
1014 | if (parent != NULL) |
---|
1015 | { |
---|
1016 | gchar* parent_xml = NULL; |
---|
1017 | gboolean success = FALSE; |
---|
1018 | |
---|
1019 | if (xml_filename) |
---|
1020 | parent_xml = g_strconcat(parent, "/%gconf.xml", NULL); |
---|
1021 | |
---|
1022 | success = create_fs_dir(parent, parent_xml, root_dir_len, |
---|
1023 | dir_mode, file_mode, err); |
---|
1024 | |
---|
1025 | if (success) |
---|
1026 | gconf_log(GCL_DEBUG, "created parent: %s", parent); |
---|
1027 | else |
---|
1028 | gconf_log(GCL_DEBUG, "failed parent: %s", parent); |
---|
1029 | |
---|
1030 | g_free(parent); |
---|
1031 | if (parent_xml) |
---|
1032 | g_free(parent_xml); |
---|
1033 | |
---|
1034 | if (!success) |
---|
1035 | return FALSE; |
---|
1036 | } |
---|
1037 | else |
---|
1038 | { |
---|
1039 | gconf_log(GCL_DEBUG, "%s has no parent", dir); |
---|
1040 | } |
---|
1041 | } |
---|
1042 | |
---|
1043 | gconf_log(GCL_DEBUG, "Making directory %s", dir); |
---|
1044 | |
---|
1045 | if (mkdir(dir, dir_mode) < 0) |
---|
1046 | { |
---|
1047 | if (errno != EEXIST) |
---|
1048 | { |
---|
1049 | gconf_set_error(err, GCONF_ERROR_FAILED, |
---|
1050 | _("Could not make directory `%s': %s"), |
---|
1051 | (gchar*)dir, strerror(errno)); |
---|
1052 | return FALSE; |
---|
1053 | } |
---|
1054 | } |
---|
1055 | |
---|
1056 | if (xml_filename != NULL) |
---|
1057 | { |
---|
1058 | int fd; |
---|
1059 | /* don't truncate the file, it may well already exist */ |
---|
1060 | fd = open(xml_filename, O_CREAT | O_WRONLY, file_mode); |
---|
1061 | |
---|
1062 | gconf_log(GCL_DEBUG, "Creating XML file %s", xml_filename); |
---|
1063 | |
---|
1064 | if (fd < 0) |
---|
1065 | { |
---|
1066 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to create file `%s': %s"), |
---|
1067 | xml_filename, strerror(errno)); |
---|
1068 | |
---|
1069 | return FALSE; |
---|
1070 | } |
---|
1071 | |
---|
1072 | if (close(fd) < 0) |
---|
1073 | { |
---|
1074 | gconf_set_error(err, GCONF_ERROR_FAILED, _("Failed to close file `%s': %s"), |
---|
1075 | xml_filename, strerror(errno)); |
---|
1076 | |
---|
1077 | return FALSE; |
---|
1078 | } |
---|
1079 | } |
---|
1080 | else |
---|
1081 | { |
---|
1082 | gconf_log(GCL_DEBUG, "No XML filename passed to create_fs_dir() for %s", dir); |
---|
1083 | } |
---|
1084 | |
---|
1085 | return TRUE; |
---|
1086 | } |
---|
1087 | |
---|
1088 | static gchar* |
---|
1089 | parent_dir(const gchar* dir) |
---|
1090 | { |
---|
1091 | /* We assume the dir doesn't have a trailing slash, since that's our |
---|
1092 | standard canonicalization in GConf */ |
---|
1093 | gchar* parent; |
---|
1094 | gchar* last_slash; |
---|
1095 | |
---|
1096 | g_return_val_if_fail(*dir != '\0', NULL); |
---|
1097 | |
---|
1098 | if (dir[1] == '\0') |
---|
1099 | { |
---|
1100 | g_assert(dir[0] == '/'); |
---|
1101 | return NULL; |
---|
1102 | } |
---|
1103 | |
---|
1104 | parent = g_strdup(dir); |
---|
1105 | |
---|
1106 | last_slash = strrchr(parent, '/'); |
---|
1107 | |
---|
1108 | /* dir must have had at least the root slash in it */ |
---|
1109 | g_assert(last_slash != NULL); |
---|
1110 | |
---|
1111 | if (last_slash != parent) |
---|
1112 | *last_slash = '\0'; |
---|
1113 | else |
---|
1114 | { |
---|
1115 | ++last_slash; |
---|
1116 | *last_slash = '\0'; |
---|
1117 | } |
---|
1118 | |
---|
1119 | return parent; |
---|
1120 | } |
---|
1121 | |
---|
1122 | |
---|
1123 | /* util */ |
---|
1124 | guint |
---|
1125 | mode_t_to_mode(mode_t orig) |
---|
1126 | { |
---|
1127 | /* I don't think this is portable. */ |
---|
1128 | guint mode = 0; |
---|
1129 | guint fullmask = S_IRWXG | S_IRWXU | S_IRWXO; |
---|
1130 | |
---|
1131 | |
---|
1132 | mode = orig & fullmask; |
---|
1133 | |
---|
1134 | g_return_val_if_fail(mode <= 0777, 0700); |
---|
1135 | |
---|
1136 | return mode; |
---|
1137 | } |
---|