source: trunk/third/GConf/backends/xml-dir.c @ 17087

Revision 17087, 25.7 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17086, which included commits to RCS files with non-trunk default branches.
Line 
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
44static void
45safe_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
62static gchar* parent_dir(const gchar* dir);
63
64struct _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
79static void
80dir_load_doc(Dir* d, GError** err);
81
82static Entry* dir_make_new_entry(Dir* d, const gchar* relative_key);
83
84static gboolean dir_forget_entry_if_useless(Dir* d, Entry* e);
85
86static Dir*
87dir_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
120Dir*
121dir_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
141Dir*
142dir_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
214static void
215entry_destroy_foreach(const gchar* name, Entry* e, gpointer data)
216{
217  entry_destroy(e);
218}
219
220void
221dir_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
238static gboolean
239create_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
244gboolean
245dir_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
264static void
265entry_sync_foreach(const gchar* name, Entry* e, gpointer data)
266{
267  entry_sync_to_node(e);
268}
269
270gboolean
271dir_sync_pending    (Dir          *d)
272{
273  return d->dirty;
274}
275
276gboolean
277dir_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
419void
420dir_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
449GTime
450dir_get_last_access (Dir          *d)
451{
452  return d->last_access;
453}
454
455GConfValue*
456dir_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
502const gchar*
503dir_get_name        (Dir          *d)
504{
505  g_return_val_if_fail(d != NULL, NULL);
506  return d->key;
507}
508
509GConfMetaInfo*
510dir_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
533void
534dir_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
582typedef struct _ListifyData ListifyData;
583
584struct _ListifyData {
585  GSList* list;
586  const gchar* name;
587  const gchar** locales;
588};
589
590static void
591listify_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
618GSList*
619dir_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
642GSList*
643dir_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
713void
714dir_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
746void
747dir_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
762gboolean
763dir_is_deleted     (Dir* d)
764{
765  return d->deleted;
766}
767
768GTime
769dir_last_access (Dir* d)
770{
771  return d->last_access;
772}
773
774/* private Dir functions */
775
776static void
777dir_fill_cache_from_doc(Dir* d);
778
779static void
780dir_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
883static Entry*
884dir_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
900static gboolean
901dir_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
920static void
921dir_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
990static gboolean
991create_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
1088static gchar*
1089parent_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 */
1124guint
1125mode_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}
Note: See TracBrowser for help on using the repository browser.