~ubuntu-branches/ubuntu/oneiric/gconf/oneiric-proposed

« back to all changes in this revision

Viewing changes to backends/xml-dir.c

  • Committer: Bazaar Package Importer
  • Author(s): Takuo KITAME
  • Date: 2002-03-17 01:51:39 UTC
  • Revision ID: james.westby@ubuntu.com-20020317015139-z4f8fdg1hoe049g0
Tags: upstream-1.0.9
ImportĀ upstreamĀ versionĀ 1.0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}