~jm-leddy/ubuntu/oneiric/gvfs/fix-899858

1.1.44 by Robert Ancell
Import upstream version 1.3.2
1
#include "config.h"
2
#include <sys/types.h>
3
#include <sys/stat.h>
4
#include <sys/mman.h>
5
#include <fcntl.h>
6
#include <string.h>
7
#include <unistd.h>
8
#include <errno.h>
9
#include <stdlib.h>
10
#include <time.h>
11
12
#if HAVE_SYS_STATFS_H
13
#include <sys/statfs.h>
14
#endif
15
#if HAVE_SYS_STATVFS_H
16
#include <sys/statvfs.h>
17
#endif
18
#if HAVE_SYS_VFS_H
19
#include <sys/vfs.h>
20
#elif HAVE_SYS_MOUNT_H
21
#if HAVE_SYS_PARAM_H
22
#include <sys/param.h>
23
#endif
24
#include <sys/mount.h>
25
#endif
26
27
#if defined(HAVE_STATFS) && defined(HAVE_STATVFS)
28
/* Some systems have both statfs and statvfs, pick the
29
   most "native" for these */
30
# if !defined(HAVE_STRUCT_STATFS_F_BAVAIL)
31
   /* on solaris and irix, statfs doesn't even have the
32
      f_bavail field */
33
#  define USE_STATVFS
34
# else
35
  /* at least on linux, statfs is the actual syscall */
36
#  define USE_STATFS
37
# endif
38
39
#elif defined(HAVE_STATFS)
40
41
# define USE_STATFS
42
43
#elif defined(HAVE_STATVFS)
44
45
# define USE_STATVFS
46
47
#endif
48
49
#include "metatree.h"
50
#include "metabuilder.h"
51
#include <glib.h>
52
#include <glib/gstdio.h>
53
#include "crc32.h"
54
55
#ifdef HAVE_LIBUDEV
56
#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE
57
#include <libudev.h>
58
#endif
59
60
#define MAGIC "\xda\x1ameta"
61
#define MAGIC_LEN 6
62
#define MAJOR_VERSION 1
63
#define MINOR_VERSION 0
64
#define JOURNAL_MAGIC "\xda\x1ajour"
65
#define JOURNAL_MAGIC_LEN 6
66
#define JOURNAL_MAJOR_VERSION 1
67
#define JOURNAL_MINOR_VERSION 0
68
69
#define KEY_IS_LIST_MASK (1<<31)
70
71
static GStaticRWLock metatree_lock = G_STATIC_RW_LOCK_INIT;
72
73
typedef enum {
74
  JOURNAL_OP_SET_KEY,
75
  JOURNAL_OP_SETV_KEY,
76
  JOURNAL_OP_UNSET_KEY,
77
  JOURNAL_OP_COPY_PATH,
78
  JOURNAL_OP_REMOVE_PATH
79
} MetaJournalEntryType;
80
81
typedef struct {
82
  guchar magic[6];
83
  guchar major;
84
  guchar minor;
85
  guint32 rotated;
86
  guint32 random_tag;
87
  guint32 root;
88
  guint32 attributes;
89
  guint64 time_t_base;
90
} MetaFileHeader;
91
92
typedef struct {
93
  guint32 name;
94
  guint32 children;
95
  guint32 metadata;
96
  guint32 last_changed;
97
} MetaFileDirEnt;
98
99
typedef struct {
100
  guint32 num_children;
101
  MetaFileDirEnt children[1];
102
} MetaFileDir;
103
104
typedef struct {
105
  guint32 num_strings;
106
  guint32 strings[1];
107
} MetaFileStringv;
108
109
typedef struct {
110
  guint32 key;
111
  guint32 value;
112
} MetaFileDataEnt;
113
114
typedef struct {
115
  guint32 num_keys;
116
  MetaFileDataEnt keys[1];
117
} MetaFileData;
118
119
typedef struct {
120
  guchar magic[6];
121
  guchar major;
122
  guchar minor;
123
  guint32 random_tag;
124
  guint32 file_size;
125
  guint32 num_entries;
126
} MetaJournalHeader;
127
128
typedef struct {
129
  guint32 entry_size;
130
  guint32 crc32;
131
  guint64 mtime;
132
  guint8 entry_type;
133
  char path[1];
134
} MetaJournalEntry;
135
136
typedef struct {
137
  char *filename;
138
  int fd;
139
  char *data;
140
  gsize len;
141
142
  MetaJournalHeader *header;
143
  MetaJournalEntry *first_entry;
144
  guint last_entry_num;
145
  MetaJournalEntry *last_entry;
146
147
  gboolean journal_valid; /* True if all entries validated on open */
148
} MetaJournal;
149
150
struct _MetaTree {
151
  volatile guint ref_count;
152
  char *filename;
153
  gboolean for_write;
154
  gboolean on_nfs;
155
156
  int fd;
157
  char *data;
158
  gsize len;
1.2.7 by Andreas Henriksson
Import upstream version 1.4.3
159
  ino_t inode;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
160
161
  guint32 tag;
162
  gint64 time_t_base;
163
  MetaFileHeader *header;
164
  MetaFileDirEnt *root;
165
166
  int num_attributes;
167
  char **attributes;
168
169
  MetaJournal *journal;
170
};
171
172
static void         meta_tree_refresh_locked   (MetaTree    *tree);
173
static MetaJournal *meta_journal_open          (MetaTree    *tree,
174
						const char  *filename,
175
						gboolean     for_write,
176
						guint32      tag);
177
static void         meta_journal_free          (MetaJournal *journal);
178
static void         meta_journal_validate_more (MetaJournal *journal);
179
180
static gpointer
181
verify_block_pointer (MetaTree *tree, guint32 pos, guint32 len)
182
{
183
  pos = GUINT32_FROM_BE (pos);
184
185
  /* Ensure 32bit aligned */
186
  if (pos %4 != 0)
187
    return NULL;
188
189
  if (pos > tree->len)
190
    return NULL;
191
192
  if (pos + len < pos ||
193
      pos + len > tree->len)
194
    return NULL;
195
196
  return tree->data + pos;
197
}
198
199
static gpointer
200
verify_array_block (MetaTree *tree, guint32 pos, gsize element_size)
201
{
202
  guint32 *nump, num;
203
204
  nump = verify_block_pointer (tree, pos, sizeof (guint32));
205
  if (nump == NULL)
206
    return NULL;
207
208
  num = GUINT32_FROM_BE (*nump);
209
210
  return verify_block_pointer (tree, pos, sizeof (guint32) + num * element_size);
211
}
212
213
static gpointer
214
verify_children_block (MetaTree *tree, guint32 pos)
215
{
216
  return verify_array_block (tree, pos, sizeof (MetaFileDirEnt));
217
}
218
219
static gpointer
220
verify_metadata_block (MetaTree *tree, guint32 pos)
221
{
222
  return verify_array_block (tree, pos, sizeof (MetaFileDataEnt));
223
}
224
225
static char *
226
verify_string (MetaTree *tree, guint32 pos)
227
{
228
  char *str, *ptr, *end;
229
230
  pos = GUINT32_FROM_BE (pos);
231
232
  if (pos > tree->len)
233
    return NULL;
234
235
  str = ptr = tree->data + pos;
236
  end = tree->data + tree->len;
237
238
  while (ptr < end && *ptr != 0)
239
    ptr++;
240
241
  if (ptr == end)
242
    return NULL;
243
244
  return str;
245
}
246
247
static void
248
meta_tree_clear (MetaTree *tree)
249
{
250
  if (tree->journal)
251
    {
252
      meta_journal_free (tree->journal);
253
      tree->journal = NULL;
254
    }
255
256
  g_free (tree->attributes);
257
  tree->num_attributes = 0;
258
  tree->attributes = NULL;
259
260
  tree->tag = 0;
261
  tree->time_t_base = 0;
262
  tree->header = NULL;
263
  tree->root = NULL;
264
265
  if (tree->data)
266
    {
267
      munmap(tree->data, tree->len);
268
      tree->data = NULL;
269
    }
270
271
  tree->len = 0;
272
  if (tree->fd != -1)
273
    {
274
      close (tree->fd);
275
      tree->fd = 0;
276
    }
277
}
278
279
static gboolean
280
is_on_nfs (char *filename)
281
{
282
#ifdef USE_STATFS
283
  struct statfs statfs_buffer;
284
  int statfs_result;
285
#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
286
  struct statvfs statfs_buffer;
287
  int statfs_result;
288
#endif
289
  char *dirname;
290
  gboolean res;
291
292
  dirname = g_path_get_dirname (filename);
293
294
  res = FALSE;
295
296
#ifdef USE_STATFS
297
298
# if STATFS_ARGS == 2
299
  statfs_result = statfs (dirname, &statfs_buffer);
300
# elif STATFS_ARGS == 4
301
  statfs_result = statfs (dirname, &statfs_buffer,
302
			  sizeof (statfs_buffer), 0);
303
# endif
304
  if (statfs_result == 0)
305
    res = statfs_buffer.f_type == 0x6969;
306
307
#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
308
  statfs_result = statvfs (dirname, &statfs_buffer);
309
310
  if (statfs_result == 0)
311
    res = strcmp (statfs_buffer.f_basetype, "nfs") == 0;
312
#endif
313
314
  g_free (dirname);
315
316
  return res;
317
}
318
319
static gboolean
320
link_to_tmp (const char *source, char *tmpl)
321
{
322
  char *XXXXXX;
323
  int count, res;
324
  static const char letters[] =
325
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
326
  static const int NLETTERS = sizeof (letters) - 1;
327
  glong value;
328
  GTimeVal tv;
329
  static int counter = 0;
330
331
  /* find the last occurrence of "XXXXXX" */
332
  XXXXXX = g_strrstr (tmpl, "XXXXXX");
333
  g_assert (XXXXXX != NULL);
334
335
  /* Get some more or less random data.  */
336
  g_get_current_time (&tv);
337
  value = (tv.tv_usec ^ tv.tv_sec) + counter++;
338
339
  for (count = 0; count < 100; value += 7777, ++count)
340
    {
341
      glong v = value;
342
343
      /* Fill in the random bits.  */
344
      XXXXXX[0] = letters[v % NLETTERS];
345
      v /= NLETTERS;
346
      XXXXXX[1] = letters[v % NLETTERS];
347
      v /= NLETTERS;
348
      XXXXXX[2] = letters[v % NLETTERS];
349
      v /= NLETTERS;
350
      XXXXXX[3] = letters[v % NLETTERS];
351
      v /= NLETTERS;
352
      XXXXXX[4] = letters[v % NLETTERS];
353
      v /= NLETTERS;
354
      XXXXXX[5] = letters[v % NLETTERS];
355
356
      res = link (source, tmpl);
357
358
      if (res >= 0)
359
	return TRUE;
360
      else if (errno != EEXIST)
361
	/* Any other error will apply also to other names we might
362
	 *  try, and there are 2^32 or so of them, so give up now.
363
	 */
364
	return FALSE;
365
    }
366
367
  return FALSE;
368
}
369
370
static int
371
safe_open (MetaTree *tree,
372
	   char *filename,
373
	   int flags)
374
{
375
  if (tree->on_nfs)
376
    {
377
      char *dirname, *tmpname;
378
      int fd, errsv;
379
380
      /* On NFS if another client unlinks an open file
381
       * it is actually removed on the server and this
382
       * client will get an ESTALE error on later access.
383
       *
384
       * For a local (i.e. on this client) unlink this is
385
       * handled by the kernel keeping track of unlinks of
386
       * open files (by this client) using ".nfsXXXX" files.
387
       *
388
       * We work around the ESTALE problem by first linking
389
       * the file to a temp file that we then unlink on
390
       * this client. We never leak the tmpfile (unless
391
       * the kernel crashes) and no other client should
392
       * remove our tmpfile.
393
       */
394
395
      dirname = g_path_get_dirname (filename);
396
      tmpname = g_build_filename (dirname, ".openXXXXXX", NULL);
397
      g_free (dirname);
398
399
      if (!link_to_tmp (filename, tmpname))
400
	fd = open (filename, flags); /* link failed, what can we do... */
401
      else
402
	{
403
	  fd = open (tmpname, flags);
404
	  errsv = errno;
405
	  unlink (tmpname);
406
	  errno = errsv;
407
	}
408
409
      g_free (tmpname);
410
      return fd;
411
    }
412
  else
413
    return open (filename, flags);
414
415
}
416
417
static gboolean
418
meta_tree_init (MetaTree *tree)
419
{
420
  struct stat statbuf;
421
  int fd;
422
  void *data;
423
  guint32 *attributes;
424
  gboolean retried;
425
  int i;
426
427
  retried = FALSE;
428
 retry:
429
  tree->on_nfs = is_on_nfs (tree->filename);
430
  fd = safe_open (tree, tree->filename, O_RDONLY);
431
  if (fd == -1)
432
    {
433
      if (tree->for_write && !retried)
434
	{
435
	  MetaBuilder *builder;
436
	  char *dir;
437
438
	  dir = g_path_get_dirname (tree->filename);
439
	  g_mkdir_with_parents (dir, 0700);
1.1.47 by Martin Pitt
Import upstream version 1.3.5
440
	  g_free (dir);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
441
442
	  builder = meta_builder_new ();
443
	  retried = TRUE;
444
	  if (meta_builder_write (builder, tree->filename))
445
	    {
446
	      meta_builder_free (builder);
447
	      goto retry;
448
	    }
449
	  meta_builder_free (builder);
450
	}
451
      tree->fd = -1;
452
      return FALSE;
453
    }
454
455
  if (fstat (fd, &statbuf) != 0 ||
456
      statbuf.st_size < sizeof (MetaFileHeader))
457
    {
458
      close (fd);
459
      return FALSE;
460
    }
461
462
  data = mmap (NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
463
  if (data == MAP_FAILED)
464
    {
465
      close (fd);
466
      return FALSE;
467
    }
468
469
  tree->fd = fd;
470
  tree->len = statbuf.st_size;
1.2.7 by Andreas Henriksson
Import upstream version 1.4.3
471
  tree->inode = statbuf.st_ino;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
472
  tree->data = data;
473
  tree->header = (MetaFileHeader *)data;
474
475
  if (memcmp (tree->header->magic, MAGIC, MAGIC_LEN) != 0)
476
    goto err;
477
478
  if (tree->header->major != MAJOR_VERSION)
479
    goto err;
480
481
  tree->root = verify_block_pointer (tree, tree->header->root, sizeof (MetaFileDirEnt));
482
  if (tree->root == NULL)
483
    goto err;
484
485
  attributes = verify_array_block (tree, tree->header->attributes, sizeof (guint32));
486
  if (attributes == NULL)
487
    goto err;
488
489
  tree->num_attributes = GUINT32_FROM_BE (*attributes);
490
  attributes++;
491
  tree->attributes = g_new (char *, tree->num_attributes);
492
  for (i = 0; i < tree->num_attributes; i++)
493
    {
494
      tree->attributes[i] = verify_string (tree, attributes[i]);
495
      if (tree->attributes[i] == NULL)
496
	goto err;
497
    }
498
499
  tree->tag = GUINT32_FROM_BE (tree->header->random_tag);
500
  tree->time_t_base = GINT64_FROM_BE (tree->header->time_t_base);
501
502
  tree->journal = meta_journal_open (tree, tree->filename, tree->for_write, tree->tag);
503
504
  /* There is a race with tree replacing, where the journal could have been
505
     deleted (and the tree replaced) inbetween opening the tree file and the
506
     journal. However we can detect this case by looking at the tree and see
507
     if its been rotated, we do this to ensure we have an uptodate tree+journal
508
     combo. */
509
  meta_tree_refresh_locked (tree);
510
511
  return TRUE;
512
513
 err:
514
  meta_tree_clear (tree);
515
  return FALSE;
516
}
517
518
MetaTree *
519
meta_tree_open (const char *filename,
520
		gboolean for_write)
521
{
522
  MetaTree *tree;
523
524
  g_assert (sizeof (MetaFileHeader) == 32);
525
  g_assert (sizeof (MetaFileDirEnt) == 16);
526
  g_assert (sizeof (MetaFileDataEnt) == 8);
527
528
  tree = g_new0 (MetaTree, 1);
529
  tree->ref_count = 1;
530
  tree->filename = g_strdup (filename);
531
  tree->for_write = for_write;
532
  tree->fd = -1;
533
534
  meta_tree_init (tree);
535
536
  return tree;
537
}
538
539
const char *
540
meta_tree_get_filename (MetaTree *tree)
541
{
542
  return tree->filename;
543
}
544
545
gboolean
546
meta_tree_exists (MetaTree *tree)
547
{
548
  return tree->fd != -1;
549
}
550
551
static GHashTable *cached_trees = NULL;
552
G_LOCK_DEFINE_STATIC (cached_trees);
553
554
MetaTree *
555
meta_tree_lookup_by_name (const char *name,
556
			  gboolean    for_write)
557
{
558
  MetaTree *tree;
559
  char *filename;
560
561
  G_LOCK (cached_trees);
562
563
  if (cached_trees == NULL)
564
    cached_trees = g_hash_table_new_full (g_str_hash,
565
					  g_str_equal,
566
					  (GDestroyNotify)g_free,
567
					  (GDestroyNotify)meta_tree_unref);
568
569
  tree = g_hash_table_lookup (cached_trees, name);
570
  if (tree && tree->for_write == for_write)
571
    {
572
      meta_tree_ref (tree);
573
      G_UNLOCK (cached_trees);
574
575
      meta_tree_refresh (tree);
576
      return tree;
577
    }
578
579
  filename = g_build_filename (g_get_user_data_dir (), "gvfs-metadata", name, NULL);
580
  tree = meta_tree_open (filename, for_write);
581
  g_free (filename);
582
583
  if (tree)
584
    g_hash_table_insert (cached_trees, g_strdup (name), meta_tree_ref (tree));
585
586
  G_UNLOCK (cached_trees);
587
588
  return tree;
589
}
590
591
MetaTree *
592
meta_tree_ref (MetaTree *tree)
593
{
154 by Sebastien Bacher
Correctly update using merge-upstream otherwise the diff will be reverted
594
  g_atomic_int_exchange_and_add ((int *)&tree->ref_count, 1);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
595
  return tree;
596
}
597
598
void
599
meta_tree_unref (MetaTree *tree)
600
{
601
  gboolean is_zero;
602
603
  is_zero = g_atomic_int_dec_and_test ((int *)&tree->ref_count);
604
  if (is_zero)
605
    {
606
      meta_tree_clear (tree);
607
      g_free (tree->filename);
608
      g_free (tree);
609
    }
610
}
611
612
static gboolean
613
meta_tree_needs_rereading (MetaTree *tree)
614
{
1.2.7 by Andreas Henriksson
Import upstream version 1.4.3
615
  struct stat statbuf;
616
1.1.44 by Robert Ancell
Import upstream version 1.3.2
617
  if (tree->fd == -1)
618
    return TRUE;
619
620
  if (tree->header != NULL &&
621
      GUINT32_FROM_BE (tree->header->rotated) == 0)
622
    return FALSE; /* Got a valid tree and its not rotated */
1.2.7 by Andreas Henriksson
Import upstream version 1.4.3
623
624
  /* Sanity check to avoid infinite loops when a stable file
625
     has the rotated bit set to 1 (see gnome bugzilla bug #600057) */
626
627
  if (lstat (tree->filename, &statbuf) != 0)
628
    return FALSE;
629
630
  if (tree->inode == statbuf.st_ino)
631
    return FALSE;
632
1.1.44 by Robert Ancell
Import upstream version 1.3.2
633
  return TRUE;
634
}
635
636
static gboolean
637
meta_tree_has_new_journal_entries (MetaTree *tree)
638
{
639
  guint32 num_entries;
640
  MetaJournal *journal;
641
642
  journal = tree->journal;
643
644
  if (journal == NULL ||
645
      !tree->journal->journal_valid)
646
    return FALSE; /* Once we've seen a failure, never look for more */
647
648
  /* TODO: Use atomic read here? */
649
  num_entries = GUINT32_FROM_BE (*(volatile guint32 *)&journal->header->num_entries);
650
651
  return journal->last_entry_num < num_entries;
652
}
653
654
655
/* Must be called with a write lock held */
656
static void
657
meta_tree_refresh_locked (MetaTree *tree)
658
{
659
  /* Needs to recheck since we dropped read lock */
660
  if (meta_tree_needs_rereading (tree))
661
    {
662
      if (tree->header)
663
	meta_tree_clear (tree);
664
      meta_tree_init (tree);
665
    }
666
  else if (meta_tree_has_new_journal_entries (tree))
667
    meta_journal_validate_more (tree->journal);
668
}
669
670
void
671
meta_tree_refresh (MetaTree *tree)
672
{
673
  gboolean needs_refresh;
674
675
  g_static_rw_lock_reader_lock (&metatree_lock);
676
  needs_refresh =
677
    meta_tree_needs_rereading (tree) ||
678
    meta_tree_has_new_journal_entries (tree);
679
  g_static_rw_lock_reader_unlock (&metatree_lock);
680
681
  if (needs_refresh)
682
    {
683
      g_static_rw_lock_writer_lock (&metatree_lock);
684
      meta_tree_refresh_locked (tree);
685
      g_static_rw_lock_writer_unlock (&metatree_lock);
686
    }
687
}
688
689
struct FindName {
690
  MetaTree *tree;
691
  const char *name;
692
};
693
694
static int
695
find_dir_element (const void *_key, const void *_dirent)
696
{
697
  const struct FindName *key = _key;
698
  const MetaFileDirEnt *dirent = _dirent;
699
  char *dirent_name;
700
701
  dirent_name = verify_string (key->tree, dirent->name);
702
  if (dirent_name == NULL)
703
    return -1;
704
  return strcmp (key->name, dirent_name);
705
}
706
707
/* modifies path!!! */
708
static MetaFileDirEnt *
709
dir_lookup_path (MetaTree *tree,
710
		 MetaFileDirEnt *dirent,
711
		 char *path)
712
{
713
  char *end_path;
714
  MetaFileDir *dir;
715
  struct FindName key;
716
717
  while (*path == '/')
718
    path++;
719
720
  if (*path == 0)
721
    return dirent;
722
723
  if (dirent->children == 0)
724
    return NULL;
725
726
  dir = verify_children_block (tree, dirent->children);
727
  if (dir == NULL)
728
    return NULL;
729
730
  end_path = path;
731
  while (*end_path != 0 &&
732
	 *end_path != '/')
733
    end_path++;
734
735
  if (*end_path != 0)
736
    *end_path++ = 0;
737
738
  key.name = path;
739
  key.tree = tree;
740
  dirent = bsearch (&key, &dir->children[0],
741
		    GUINT32_FROM_BE (dir->num_children), sizeof (MetaFileDirEnt),
742
		    find_dir_element);
743
744
  if (dirent == NULL)
745
    return NULL;
746
747
  return dir_lookup_path (tree, dirent, end_path);
748
}
749
750
static MetaFileDirEnt *
751
meta_tree_lookup (MetaTree *tree,
752
		  const char *path)
753
{
754
  MetaFileDirEnt *dirent;
755
  char *path_copy;
756
757
  if (tree->root == NULL)
758
    return NULL;
759
760
  path_copy = g_strdup (path);
761
  dirent = dir_lookup_path (tree, tree->root, path_copy);
762
  g_free (path_copy);
763
764
  return dirent;
765
}
766
767
static MetaFileData *
768
meta_tree_lookup_data (MetaTree *tree,
769
		       const char *path)
770
{
771
  MetaFileDirEnt *dirent;
772
  MetaFileData *data;
773
774
  data = NULL;
775
  dirent = meta_tree_lookup (tree, path);
776
  if (dirent)
777
    data = verify_metadata_block (tree, dirent->metadata);
778
779
  return data;
780
}
781
782
static int
783
find_attribute_id (const void *_key, const void *_entry)
784
{
785
  const char *key = _key;
786
  const char *const*entry = _entry;
787
788
  return strcmp (key, *entry);
789
}
790
791
#define NO_KEY ((guint32)-1)
792
793
static guint32
794
get_id_for_key (MetaTree *tree,
795
		const char *attribute)
796
{
797
  char **attribute_ptr;
798
799
  attribute_ptr = bsearch (attribute, tree->attributes,
800
			   tree->num_attributes, sizeof (char *),
801
			   find_attribute_id);
802
803
  if (attribute_ptr == NULL)
804
    return NO_KEY;
805
806
  return attribute_ptr - tree->attributes;
807
}
808
809
struct FindId {
810
  MetaTree *tree;
811
  guint32 id;
812
};
813
814
static int
815
find_data_element (const void *_key, const void *_dataent)
816
{
817
  const struct FindId *key = _key;
818
  const MetaFileDataEnt *dataent = _dataent;
819
  guint32 key_id;
820
821
  key_id = GUINT32_FROM_BE (dataent->key) & ~KEY_IS_LIST_MASK;
822
823
  return key->id - key_id;
824
}
825
826
static MetaFileDataEnt *
827
meta_data_get_key (MetaTree *tree,
828
		   MetaFileData *data,
829
		   const char *attribute)
830
{
831
  MetaFileDataEnt *dataent;
832
  struct FindId key;
833
834
  key.id = get_id_for_key (tree, attribute);
835
  key.tree = tree;
836
  dataent = bsearch (&key, &data->keys[0],
837
		     GUINT32_FROM_BE (data->num_keys), sizeof (MetaFileDataEnt),
838
		     find_data_element);
839
840
  return dataent;
841
}
842
843
static char *
844
get_journal_filename (const char *filename, guint32 random_tag)
845
{
846
  const char *hexdigits = "0123456789abcdef";
847
  char tag[9];
848
  int i;
849
850
  for (i = 7; i >= 0; i--)
851
    {
852
      tag[i] = hexdigits[random_tag % 0x10];
853
      random_tag >>= 4;
854
    }
855
856
  tag[8] = 0;
857
858
  return g_strconcat (filename, "-", tag, ".log", NULL);
859
}
860
861
static void
862
meta_journal_free (MetaJournal *journal)
863
{
864
  g_free (journal->filename);
865
  munmap(journal->data, journal->len);
866
  close (journal->fd);
867
  g_free (journal);
868
}
869
870
static MetaJournalEntry *
871
verify_journal_entry (MetaJournal *journal,
872
		      MetaJournalEntry *entry)
873
{
874
  guint32 offset, real_crc32;
875
  guint32 entry_len, entry_len_end;
876
  char *ptr;
877
878
  ptr = (char *)entry;
879
  if (ptr < journal->data)
880
    return NULL;
881
  offset =  ptr - journal->data;
882
883
  /* Must be 32bit aligned */
884
  if (offset % 4 != 0)
885
    return NULL;
886
887
  /* entry_size must be valid */
888
  if (offset > journal->len - 4)
889
    return NULL;
890
891
  /* Verify that entry fits and has right size */
892
  entry_len = GUINT32_FROM_BE (entry->entry_size);
893
894
  /* Must be 32bit aligned */
895
  if (entry_len % 4 != 0)
896
    return NULL;
897
  /* Must have space for at the very least:
898
     len+crc32+mtime+type+path_terminating_zeor+end_len */
899
  if (journal->len < 4 + 4 + 8 + 1 + 1 + 4)
900
    return NULL;
901
902
  if (entry_len > journal->len ||
903
      offset > journal->len - entry_len)
904
    return NULL;
905
906
  entry_len_end = GUINT32_FROM_BE (*(guint32 *)(journal->data + offset + entry_len - 4));
907
  if (entry_len != entry_len_end)
908
    return NULL;
909
1.1.46 by Sebastien Bacher
Import upstream version 1.3.4
910
  real_crc32 = metadata_crc32 (journal->data + offset + 8, entry_len - 8);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
911
  if (real_crc32 != GUINT32_FROM_BE (entry->crc32))
912
    return NULL;
913
914
  return (MetaJournalEntry *)(journal->data + offset + entry_len);
915
}
916
917
/* Try to validate more entries, call with writer lock */
918
static void
919
meta_journal_validate_more (MetaJournal *journal)
920
{
921
  guint32 num_entries, i;
922
  MetaJournalEntry *entry, *next_entry;
923
924
  if (!journal->journal_valid)
925
    return; /* Once we've seen a failure, never look for more */
926
927
  /* TODO: Use atomic read here? */
928
  num_entries = GUINT32_FROM_BE (*(volatile guint32 *)&journal->header->num_entries);
929
930
  entry = journal->last_entry;
931
  i = journal->last_entry_num;
932
  while (i < num_entries)
933
    {
934
      next_entry = verify_journal_entry (journal, entry);
935
936
      if (next_entry == NULL)
937
	{
938
	  journal->journal_valid = FALSE;
939
	  break;
940
	}
941
942
      entry = next_entry;
943
      i++;
944
    }
945
946
  journal->last_entry = entry;
947
  journal->last_entry_num = i;
948
}
949
950
static void
951
set_uint32 (GString *s, guint32 offset, guint32 val)
952
{
953
  union {
954
    guint32 as_int;
955
    char as_bytes[4];
956
  } u;
957
958
  u.as_int = GUINT32_TO_BE (val);
959
  memcpy (s->str + offset, u.as_bytes, 4);
960
}
961
962
static GString *
963
append_uint32 (GString *s, guint32 val)
964
{
965
  union {
966
    guint32 as_int;
967
    char as_bytes[4];
968
  } u;
969
970
  u.as_int = GUINT32_TO_BE (val);
971
  g_string_append_len (s, u.as_bytes, 4);
972
  return s;
973
}
974
975
static GString *
976
append_uint64 (GString *s, guint64 val)
977
{
978
  union {
979
    guint64 as_int;
980
    char as_bytes[8];
981
  } u;
982
983
  u.as_int = GUINT64_TO_BE (val);
984
  g_string_append_len (s, u.as_bytes, 8);
985
  return s;
986
}
987
988
static GString *
989
append_string (GString *s, const char *str)
990
{
991
  g_string_append (s, str);
992
  g_string_append_c (s, 0);
993
  return s;
994
}
995
996
static guint64
997
get_time_t (MetaTree *tree, guint32 val)
998
{
999
  val = GUINT32_FROM_BE (val);
1000
  if (val == 0)
1001
    return 0;
1002
  return val + tree->time_t_base;
1003
}
1004
1005
static GString *
1006
meta_journal_entry_init (int op,
1007
			 guint64 mtime,
1008
			 const char *path)
1009
{
1010
  GString *out;
1011
1012
  out = g_string_new (NULL);
1013
  append_uint32 (out, 0); /* len */
1014
  append_uint32 (out, 0); /* crc32 */
1015
  append_uint64 (out, mtime);
1016
  g_string_append_c (out, (char)op);
1017
  append_string (out, path);
1018
1019
  return out;
1020
}
1021
1022
static GString *
1023
meta_journal_entry_finish (GString *out)
1024
{
1025
  guint32 len;
1026
1027
  while (out->len % 4 != 0)
1028
    g_string_append_c (out, 0);
1029
1030
  len = out->len + 4;
1031
  append_uint32 (out, len);
1032
  set_uint32 (out, 0, len);
1.1.46 by Sebastien Bacher
Import upstream version 1.3.4
1033
  set_uint32 (out, 4, metadata_crc32 (out->str + 8, len - 8));
1.1.44 by Robert Ancell
Import upstream version 1.3.2
1034
  return out;
1035
}
1036
1037
static GString *
1038
meta_journal_entry_new_set (guint64 mtime,
1039
			    const char *path,
1040
			    const char *key,
1041
			    const char *value)
1042
{
1043
  GString *out;
1044
1045
  out = meta_journal_entry_init (JOURNAL_OP_SET_KEY, mtime, path);
1046
  append_string (out, key);
1047
  append_string (out, value);
1048
  return meta_journal_entry_finish (out);
1049
}
1050
1051
static GString *
1052
meta_journal_entry_new_setv (guint64 mtime,
1053
			     const char *path,
1054
			     const char *key,
1055
			     char      **value)
1056
{
1057
  GString *out;
1058
  int i;
1059
1060
  out = meta_journal_entry_init (JOURNAL_OP_SETV_KEY, mtime, path);
1061
  append_string (out, key);
1062
1063
  /* Pad to 32bit */
1064
  while (out->len % 4 != 0)
1065
    g_string_append_c (out, 0);
1066
1067
  append_uint32 (out, g_strv_length ((char **)value));
1068
  for (i = 0; value[i] != NULL; i++)
1069
    append_string (out, value[i]);
1070
1071
  return meta_journal_entry_finish (out);
1072
}
1073
1074
static GString *
1075
meta_journal_entry_new_remove (guint64 mtime,
1076
			       const char *path)
1077
{
1078
  GString *out;
1079
1080
  out = meta_journal_entry_init (JOURNAL_OP_REMOVE_PATH, mtime, path);
1081
  return meta_journal_entry_finish (out);
1082
}
1083
1084
static GString *
1085
meta_journal_entry_new_copy (guint64 mtime,
1086
			     const char *src,
1087
			     const char *dst)
1088
{
1089
  GString *out;
1090
1091
  out = meta_journal_entry_init (JOURNAL_OP_COPY_PATH, mtime, dst);
1092
  append_string (out, src);
1093
  return meta_journal_entry_finish (out);
1094
}
1095
1096
static GString *
1097
meta_journal_entry_new_unset (guint64 mtime,
1098
			      const char *path,
1099
			      const char *key)
1100
{
1101
  GString *out;
1102
1103
  out = meta_journal_entry_init (JOURNAL_OP_UNSET_KEY, mtime, path);
1104
  append_string (out, key);
1105
  return meta_journal_entry_finish (out);
1106
}
1107
1108
1109
/* Call with writer lock held */
1110
static gboolean
1111
meta_journal_add_entry (MetaJournal *journal,
1112
			GString *entry)
1113
{
1114
  char *ptr;
1115
  guint32 offset;
1116
1117
  g_assert (journal->journal_valid);
1118
1119
  ptr = (char *)journal->last_entry;
1120
  offset =  ptr - journal->data;
1121
1122
  /* Does the entry fit? */
1123
  if (entry->len > journal->len - offset)
1124
    return FALSE;
1125
1126
  memcpy (ptr, entry->str, entry->len);
1127
1128
  journal->header->num_entries = GUINT_TO_BE (journal->last_entry_num + 1);
1129
  meta_journal_validate_more (journal);
1130
  g_assert (journal->journal_valid);
1131
1132
  return TRUE;
1133
}
1134
1135
static MetaJournal *
1136
meta_journal_open (MetaTree *tree, const char *filename, gboolean for_write, guint32 tag)
1137
{
1138
  MetaJournal *journal;
1139
  struct stat statbuf;
1140
  int fd;
1141
  char *data;
1142
  char *journal_filename;
1143
  int open_flags, mmap_prot;
1144
1145
  g_assert (sizeof (MetaJournalHeader) == 20);
1146
1147
  journal_filename = get_journal_filename (filename, tag);
1148
1149
  if (for_write)
1150
    open_flags = O_RDWR;
1151
  else
1152
    open_flags = O_RDONLY;
1153
1154
  fd = safe_open (tree, journal_filename, open_flags);
1155
  g_free (journal_filename);
1156
  if (fd == -1)
1157
    return NULL;
1158
1159
  if (fstat (fd, &statbuf) != 0 ||
1160
      statbuf.st_size < sizeof (MetaJournalHeader))
1161
    {
1162
      close (fd);
1163
      return NULL;
1164
    }
1165
1166
  mmap_prot = PROT_READ;
1167
  if (for_write)
1168
    mmap_prot |= PROT_WRITE;
1169
  data = mmap (NULL, statbuf.st_size, mmap_prot, MAP_SHARED, fd, 0);
1170
  if (data == MAP_FAILED)
1171
    {
1172
      close (fd);
1173
      return NULL;
1174
    }
1175
1176
  journal = g_new0 (MetaJournal, 1);
1177
  journal->filename = g_strdup (filename);
1178
  journal->fd = fd;
1179
  journal->len = statbuf.st_size;
1180
  journal->data = data;
1181
  journal->header = (MetaJournalHeader *)data;
1182
  journal->first_entry = (MetaJournalEntry *)(data + sizeof (MetaJournalHeader));
1183
  journal->last_entry = journal->first_entry;
1184
  journal->last_entry_num = 0;
1185
1186
  if (memcmp (journal->header->magic, JOURNAL_MAGIC, JOURNAL_MAGIC_LEN) != 0)
1187
    goto err;
1188
1189
  if (journal->header->major != JOURNAL_MAJOR_VERSION)
1190
    goto err;
1191
1192
  if (journal->len != GUINT32_FROM_BE (journal->header->file_size))
1193
    goto err;
1194
1195
  if (tag != GUINT32_FROM_BE (journal->header->random_tag))
1196
    goto err;
1197
1198
  journal->journal_valid = TRUE;
1199
  meta_journal_validate_more (journal);
1200
1201
  return journal;
1202
1203
 err:
1204
  meta_journal_free (journal);
1205
  return NULL;
1206
}
1207
1208
static char *
1209
get_next_arg (char *str)
1210
{
1211
  return str  + strlen (str) + 1;
1212
}
1213
1214
static gboolean
1215
journal_entry_is_key_type (MetaJournalEntry *entry)
1216
{
1217
 return
1218
   entry->entry_type == JOURNAL_OP_SET_KEY ||
1219
   entry->entry_type == JOURNAL_OP_SETV_KEY ||
1220
   entry->entry_type == JOURNAL_OP_UNSET_KEY;
1221
}
1222
1223
static gboolean
1224
journal_entry_is_path_type (MetaJournalEntry *entry)
1225
{
1226
 return
1227
   entry->entry_type == JOURNAL_OP_COPY_PATH ||
1228
   entry->entry_type == JOURNAL_OP_REMOVE_PATH;
1229
}
1230
1231
/* returns remainer if path has "prefix" as prefix (or is equal to prefix) */
1232
static const char *
1233
get_prefix_match (const char *path,
1234
		  const char *prefix)
1235
{
1236
  gsize prefix_len;
1237
  const char *remainder;
1238
1239
  prefix_len = strlen (prefix);
1240
1241
  /* Handle trailing slashes in prefix, this is not
1242
     generally common, but happens in the case of the
1243
     root dir "/" */
1244
  while (prefix_len > 0 &&
1245
	 prefix[prefix_len-1] == '/')
1246
    prefix_len--;
1247
1248
  if (strncmp (path, prefix, prefix_len) != 0)
1249
    return NULL;
1250
1251
  remainder = path + prefix_len;
1252
  if (*remainder != 0 &&
1253
      *remainder != '/')
1254
    return NULL; /* only a string prefix, not a path prefix */
1255
1256
  while (*remainder == '/')
1257
    remainder++;
1258
1259
  return remainder;
1260
}
1261
1262
typedef gboolean (*journal_key_callback) (MetaJournal *journal,
1263
					  MetaJournalEntryType entry_type,
1264
					  const char *path,
1265
					  guint64 mtime,
1266
					  const char *key,
1267
					  gpointer value,
1268
					  char **iter_path,
1269
					  gpointer user_data);
1270
typedef gboolean (*journal_path_callback) (MetaJournal *journal,
1271
					   MetaJournalEntryType entry_type,
1272
					   const char *path,
1273
					   guint64 mtime,
1274
					   const char *source_path,
1275
					   char **iter_path,
1276
					   gpointer user_data);
1277
1278
static char *
1279
meta_journal_iterate (MetaJournal *journal,
1280
		      const char *path,
1281
		      journal_key_callback key_callback,
1282
		      journal_path_callback path_callback,
1283
		      gpointer user_data)
1284
{
1285
  MetaJournalEntry *entry;
1286
  guint32 *sizep;
1287
  char *journal_path, *journal_key, *source_path;
1288
  char *path_copy, *value;
1289
  gboolean res;
1290
  guint64 mtime;
1291
1292
  path_copy = g_strdup (path);
1293
1294
  if (journal == NULL)
1295
    return path_copy;
1296
1297
  entry = journal->last_entry;
1298
  while (entry > journal->first_entry)
1299
    {
1300
      sizep = (guint32 *)entry;
1301
      entry = (MetaJournalEntry *)((char *)entry - GUINT32_FROM_BE (*(sizep-1)));
1302
1303
      mtime = GUINT64_FROM_BE (entry->mtime);
1304
      journal_path = &entry->path[0];
1305
1306
      if (journal_entry_is_key_type (entry) &&
1307
	  key_callback) /* set, setv or unset */
1308
	{
1309
	  journal_key = get_next_arg (journal_path);
1310
	  value = get_next_arg (journal_key);
1311
1312
	  /* Only affects is path is exactly the same */
1313
	  res = key_callback (journal, entry->entry_type,
1314
			      journal_path, mtime, journal_key,
1315
			      value,
1316
			      &path_copy, user_data);
1317
	  if (!res)
1318
	    {
1319
	      g_free (path_copy);
1320
	      return NULL;
1321
	    }
1322
	}
1323
      else if (journal_entry_is_path_type (entry) &&
1324
	       path_callback) /* copy or remove */
1325
	{
1326
	  source_path = NULL;
1327
	  if (entry->entry_type == JOURNAL_OP_COPY_PATH)
1328
	    source_path = get_next_arg (journal_path);
1329
1330
	  res = path_callback (journal, entry->entry_type,
1331
			       journal_path, mtime, source_path,
1332
			       &path_copy, user_data);
1333
	  if (!res)
1334
	    {
1335
	      g_free (path_copy);
1336
	      return NULL;
1337
	    }
1338
	}
1339
      else
1340
	g_warning ("Unknown journal entry type %d\n", entry->entry_type);
1341
    }
1342
1343
  return path_copy;
1344
}
1345
1346
typedef struct {
1347
  const char *key;
1348
  MetaKeyType type;
1349
  guint64 mtime;
1350
  gpointer value;
1351
} PathKeyData;
1352
1353
static gboolean
1354
journal_iter_key (MetaJournal *journal,
1355
		  MetaJournalEntryType entry_type,
1356
		  const char *path,
1357
		  guint64 mtime,
1358
		  const char *key,
1359
		  gpointer value,
1360
		  char **iter_path,
1361
		  gpointer user_data)
1362
{
1363
  PathKeyData *data = user_data;
1364
1365
  if (strcmp (path, *iter_path) != 0)
1366
    return TRUE; /* No match, continue */
1367
1368
  data->mtime = mtime;
1369
1370
  if (data->key == NULL)
1371
    return FALSE; /* Matched path, not interested in key, stop iterating */
1372
1373
  if (strcmp (data->key, key) != 0)
1374
    return TRUE; /* No match, continue */
1375
1376
  switch (entry_type)
1377
    {
1378
    case JOURNAL_OP_SET_KEY:
1379
      data->type = META_KEY_TYPE_STRING;
1380
      data->value = value;
1381
      break;
1382
    case JOURNAL_OP_SETV_KEY:
1383
      data->type = META_KEY_TYPE_STRINGV;
1384
      data->value = value;
1385
      break;
1386
    case JOURNAL_OP_UNSET_KEY:
1387
      data->type = META_KEY_TYPE_NONE;
1388
      data->value = NULL;
1389
      break;
1390
    default:
1391
      /* No other key type should reach this  */
1392
      g_assert_not_reached ();
1393
    }
1394
  return FALSE; /* stop iterating */
1395
}
1396
1397
static gboolean
1398
journal_iter_path (MetaJournal *journal,
1399
		   MetaJournalEntryType entry_type,
1400
		   const char *path,
1401
		   guint64 mtime,
1402
		   const char *source_path,
1403
		   char **iter_path,
1404
		   gpointer user_data)
1405
{
1406
  PathKeyData *data = user_data;
1407
  char *old_path;
1408
  const char *remainder;
1409
1410
  /* is this a parent of the iter path */
1411
  remainder = get_prefix_match (*iter_path, path);
1412
  if (remainder == NULL)
1413
    return TRUE; /* Not related, continue */
1414
1415
  /* path is affected as a child of this node */
1416
  if (entry_type == JOURNAL_OP_REMOVE_PATH)
1417
    {
1418
      if (data)
1419
	{
1420
	  data->mtime = mtime;
1421
	  data->type = META_KEY_TYPE_NONE;
1422
	  data->value = NULL;
1423
	}
1424
      return FALSE; /* stop iterating */
1425
    }
1426
  else if (entry_type == JOURNAL_OP_COPY_PATH)
1427
    {
1428
      old_path = *iter_path;
1429
      *iter_path = g_build_filename (source_path, remainder, NULL);
1430
      g_free (old_path);
1431
      return TRUE; /* Continue, with new path */
1432
    }
1433
  return TRUE;
1434
}
1435
1436
static char *
1437
meta_journal_reverse_map_path_and_key (MetaJournal *journal,
1438
				       const char *path,
1439
				       const char *key,
1440
				       MetaKeyType *type,
1441
				       guint64 *mtime,
1442
				       gpointer *value)
1443
{
154 by Sebastien Bacher
Correctly update using merge-upstream otherwise the diff will be reverted
1444
  PathKeyData data = {NULL};
1.1.44 by Robert Ancell
Import upstream version 1.3.2
1445
  char *res_path;
1446
1447
  data.key = key;
1448
  res_path = meta_journal_iterate (journal,
1449
				   path,
1450
				   journal_iter_key,
1451
				   journal_iter_path,
1452
				   &data);
1453
  *type = data.type;
1454
  if (mtime)
1455
    *mtime = data.mtime;
1456
  *value = data.value;
1457
  return res_path;
1458
}
1459
1460
MetaKeyType
1461
meta_tree_lookup_key_type  (MetaTree                         *tree,
1462
			    const char                       *path,
1463
			    const char                       *key)
1464
{
1465
  MetaFileData *data;
1466
  MetaFileDataEnt *ent;
1467
  char *new_path;
1468
  MetaKeyType type;
1469
  gpointer value;
1470
1471
  g_static_rw_lock_reader_lock (&metatree_lock);
1472
1473
  new_path = meta_journal_reverse_map_path_and_key (tree->journal,
1474
						    path,
1475
						    key,
1476
						    &type, NULL, &value);
1477
  if (new_path == NULL)
1478
    goto out; /* type is set */
1479
1480
  data = meta_tree_lookup_data (tree, new_path);
1481
  ent = NULL;
1482
  if (data)
1483
    ent = meta_data_get_key (tree, data, key);
1484
1485
  g_free (new_path);
1486
1487
  if (ent == NULL)
1488
    type = META_KEY_TYPE_NONE;
1489
  else if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK)
1490
    type = META_KEY_TYPE_STRINGV;
1491
  else
1492
    type = META_KEY_TYPE_STRING;
1493
1494
 out:
1495
  g_static_rw_lock_reader_unlock (&metatree_lock);
1496
  return type;
1497
}
1498
1499
guint64
1500
meta_tree_get_last_changed (MetaTree *tree,
1501
			    const char *path)
1502
{
1503
  MetaFileDirEnt *dirent;
1504
  MetaKeyType type;
1505
  char *new_path;
1506
  gpointer value;
1507
  guint64 res, mtime;
1508
1509
  g_static_rw_lock_reader_lock (&metatree_lock);
1510
1511
  new_path = meta_journal_reverse_map_path_and_key (tree->journal,
1512
						    path,
1513
						    NULL,
1514
						    &type, &mtime, &value);
1515
  if (new_path == NULL)
1516
    {
1517
      res = mtime;
1518
      goto out;
1519
    }
1520
1521
  res = 0;
1522
  dirent = meta_tree_lookup (tree, new_path);
1523
  if (dirent)
1524
    res = get_time_t (tree, dirent->last_changed);
1525
1526
  g_free (new_path);
1527
1528
 out:
1529
  g_static_rw_lock_reader_unlock (&metatree_lock);
1530
1531
  return res;
1532
}
1533
1534
char *
1535
meta_tree_lookup_string (MetaTree   *tree,
1536
			 const char *path,
1537
			 const char *key)
1538
{
1539
  MetaFileData *data;
1540
  MetaFileDataEnt *ent;
1541
  MetaKeyType type;
1542
  gpointer value;
1543
  char *new_path;
1544
  char *res;
1545
1546
  g_static_rw_lock_reader_lock (&metatree_lock);
1547
1548
  new_path = meta_journal_reverse_map_path_and_key (tree->journal,
1549
						    path,
1550
						    key,
1551
						    &type, NULL, &value);
1552
  if (new_path == NULL)
1553
    {
1554
      res = NULL;
1555
      if (type == META_KEY_TYPE_STRING)
1556
	res = g_strdup (value);
1557
      goto out;
1558
    }
1559
1560
  data = meta_tree_lookup_data (tree, new_path);
1561
  ent = NULL;
1562
  if (data)
1563
    ent = meta_data_get_key (tree, data, key);
1564
1565
  g_free (new_path);
1566
1567
  if (ent == NULL)
1568
    res = NULL;
1569
  else if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK)
1570
    res = NULL;
1571
  else
1572
    res = g_strdup (verify_string (tree, ent->value));
1573
1574
 out:
1575
  g_static_rw_lock_reader_unlock (&metatree_lock);
1576
1577
  return res;
1578
}
1579
1580
static char **
1581
get_stringv_from_journal (gpointer value,
1582
			  gboolean dup_strings)
1583
{
1584
  char *valuep = value;
1585
  guint32 num_strings, i;
1586
  char **res;
1587
1588
  while (((gsize)valuep) % 4 != 0)
1589
    valuep++;
1590
1591
  num_strings = GUINT32_FROM_BE (*(guint32 *)valuep);
1592
  valuep += 4;
1593
1594
  res = g_new (char *, num_strings + 1);
1595
1596
  for (i = 0; i < num_strings; i++)
1597
    {
1598
      if (dup_strings)
1599
	res[i] = g_strdup (valuep);
1600
      else
1601
	res[i] = valuep;
1602
      valuep = get_next_arg (valuep);
1603
    }
1604
1605
  res[i] = NULL;
1606
1607
  return res;
1608
}
1609
1610
char **
1611
meta_tree_lookup_stringv   (MetaTree                         *tree,
1612
			    const char                       *path,
1613
			    const char                       *key)
1614
{
1615
  MetaFileData *data;
1616
  MetaFileDataEnt *ent;
1617
  MetaKeyType type;
1618
  MetaFileStringv *stringv;
1619
  gpointer value;
1620
  char *new_path;
1621
  char **res;
1622
  guint32 num_strings, i;
1623
1624
  g_static_rw_lock_reader_lock (&metatree_lock);
1625
1626
  new_path = meta_journal_reverse_map_path_and_key (tree->journal,
1627
						    path,
1628
						    key,
1629
						    &type, NULL, &value);
1630
  if (new_path == NULL)
1631
    {
1632
      res = NULL;
1633
      if (type == META_KEY_TYPE_STRINGV)
1634
	res = get_stringv_from_journal (value, TRUE);
1635
      goto out;
1636
    }
1637
1638
  data = meta_tree_lookup_data (tree, new_path);
1639
  ent = NULL;
1640
  if (data)
1641
    ent = meta_data_get_key (tree, data, key);
1642
1643
  g_free (new_path);
1644
1645
  if (ent == NULL)
1646
    res = NULL;
1647
  else if ((GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK) == 0)
1648
    res = NULL;
1649
  else
1650
    {
1651
      stringv = verify_array_block (tree, ent->value,
1652
				    sizeof (guint32));
1653
      num_strings = GUINT32_FROM_BE (stringv->num_strings);
1654
      res = g_new (char *, num_strings + 1);
1655
      for (i = 0; i < num_strings; i++)
1.2.7 by Andreas Henriksson
Import upstream version 1.4.3
1656
	res[i] = g_strdup (verify_string (tree, stringv->strings[i]));
1.1.44 by Robert Ancell
Import upstream version 1.3.2
1657
      res[i] = NULL;
1658
    }
1659
1660
 out:
1661
  g_static_rw_lock_reader_unlock (&metatree_lock);
1662
1663
  return res;
1664
}
1665
1666
typedef struct {
1667
  char *name;
1668
  guint64 last_changed;
1669
  gboolean has_children;
1670
  gboolean has_data;
1671
  gboolean exists; /* May be true even if deleted is true, if recreated */
1672
  gboolean deleted; /* Was deleted at some point, ignore everything before */
1673
1674
  gboolean reported; /* Set to true when reported to user */
1675
} EnumDirChildInfo;
1676
1677
typedef struct {
1678
  GHashTable *children;
1679
} EnumDirData;
1680
1681
1682
static void
1683
child_info_free (EnumDirChildInfo *info)
1684
{
1685
  g_free (info->name);
1686
  g_free (info);
1687
}
1688
1689
static EnumDirChildInfo *
1690
get_child_info (EnumDirData *data,
1691
		const char *remainder,
1692
		gboolean *direct_child)
1693
{
1694
  EnumDirChildInfo *info;
1695
  const char *slash;
1696
  char *name;
1697
1698
  slash = strchr (remainder, '/');
154 by Sebastien Bacher
Correctly update using merge-upstream otherwise the diff will be reverted
1699
  if (slash != NULL)
1.1.44 by Robert Ancell
Import upstream version 1.3.2
1700
    name = g_strndup (remainder, slash - remainder);
1701
  else
1702
    name = g_strdup (remainder);
1703
1704
  *direct_child = slash == NULL;
1705
1706
  info = g_hash_table_lookup (data->children, name);
1707
  if (info == NULL)
1708
    {
1709
      info = g_new0 (EnumDirChildInfo, 1);
1710
      info->name = name;
1711
      g_hash_table_insert (data->children, info->name, info);
1712
    }
1713
  else
1714
    g_free (name);
1715
1716
  return info;
1717
}
1718
1719
static gboolean
1720
enum_dir_iter_key (MetaJournal *journal,
1721
		   MetaJournalEntryType entry_type,
1722
		   const char *path,
1723
		   guint64 mtime,
1724
		   const char *key,
1725
		   gpointer value,
1726
		   char **iter_path,
1727
		   gpointer user_data)
1728
{
1729
  EnumDirData *data = user_data;
1730
  EnumDirChildInfo *info;
1731
  gboolean direct_child;
1732
  const char *remainder;
1733
1734
  /* is this a true child of iter_path, then that may create a child */
1735
  remainder = get_prefix_match (path, *iter_path);
1736
  if (remainder != NULL && *remainder != 0)
1737
    {
1738
      info = get_child_info (data, remainder, &direct_child);
1739
1740
      if (!info->deleted)
1741
	{
1742
	  info->exists = TRUE;
1743
	  if (info->last_changed == 0)
1744
	    info->last_changed = mtime;
1745
	  info->has_children |= !direct_child;
1746
	  info->has_data |=
1747
	    direct_child && entry_type != JOURNAL_OP_UNSET_KEY;
1748
	}
1749
    }
1750
1751
  return TRUE; /* continue */
1752
}
1753
1754
static gboolean
1755
enum_dir_iter_path (MetaJournal *journal,
1756
		    MetaJournalEntryType entry_type,
1757
		    const char *path,
1758
		    guint64 mtime,
1759
		    const char *source_path,
1760
		    char **iter_path,
1761
		    gpointer user_data)
1762
{
1763
  EnumDirData *data = user_data;
1764
  EnumDirChildInfo *info;
1765
  gboolean direct_child;
1766
  const char *remainder;
1767
  char *old_path;
1768
1769
  /* Is path a true child of iter_path */
1770
  remainder = get_prefix_match (path, *iter_path);
1771
  if (remainder != NULL && *remainder != 0)
1772
    {
1773
      info = get_child_info (data, remainder, &direct_child);
1774
1775
	/* copy destination a true child, that creates a child */
1776
      if (entry_type == JOURNAL_OP_COPY_PATH)
1777
	{
1778
	  if (!info->deleted)
1779
	    {
1780
	      info->exists = TRUE;
1781
	      if (info->last_changed == 0)
1782
		info->last_changed = mtime;
1783
	      info->has_children = TRUE;
1784
	      info->has_data = TRUE;
1785
	    }
1786
	}
1787
      else if (entry_type == JOURNAL_OP_REMOVE_PATH &&
1788
	       direct_child)
1789
	{
1790
	  info->deleted = TRUE;
1791
	}
1792
    }
1793
1794
  /* is this a parent of the iter path */
1795
  remainder = get_prefix_match (*iter_path, path);
1796
  if (remainder != NULL)
1797
    {
1798
      /* path is affected as a child of this node */
1799
      if (entry_type == JOURNAL_OP_REMOVE_PATH)
1800
	return FALSE; /* stop iterating */
1801
      else if (entry_type == JOURNAL_OP_COPY_PATH)
1802
	{
1803
	  old_path = *iter_path;
1804
	  *iter_path = g_build_filename (source_path, remainder, NULL);
1805
	  g_free (old_path);
1806
	  return TRUE; /* Continue, with new path */
1807
	}
1808
    }
1809
1810
  return TRUE;
1811
}
1812
1813
static gboolean
1814
enumerate_dir (MetaTree *tree,
1815
	       MetaFileDir *dir,
1816
	       GHashTable *children,
1817
	       meta_tree_dir_enumerate_callback callback,
1818
	       gpointer user_data)
1819
{
1820
  guint32 i, num_children;
1821
  MetaFileDirEnt *dirent;
1822
  EnumDirChildInfo *info;
1823
  char *dirent_name;
1824
  gboolean has_children;
1825
  gboolean has_data;
1826
  guint64 last_changed;
1827
1828
  num_children = GUINT32_FROM_BE (dir->num_children);
1829
  for (i = 0; i < num_children; i++)
1830
    {
1831
      dirent = &dir->children[i];
1832
      dirent_name = verify_string (tree, dirent->name);
1833
      if (dirent_name == NULL)
1834
	continue;
1835
1836
      last_changed = get_time_t (tree, dirent->last_changed);
1837
      has_children = dirent->children != 0;
1838
      has_data = dirent->metadata != 0;
1839
1840
      info = g_hash_table_lookup (children, dirent_name);
1841
      if (info)
1842
	{
1843
	  if (info->deleted)
1844
	    continue; /* if recreated (i.e. exists == TRUE), report later */
1845
1846
	  info->reported = TRUE;
1847
1848
	  if (info->last_changed != 0)
1849
	    last_changed = MAX (last_changed, info->last_changed);
1850
1851
	  has_children |= info->has_children;
1852
	  has_data |= info->has_data;
1853
	}
1854
1855
      if (!callback (dirent_name,
1856
		     last_changed,
1857
		     has_children,
1858
		     has_data,
1859
		     user_data))
1860
	return FALSE;
1861
    }
1862
  return TRUE;
1863
}
1864
1865
void
1866
meta_tree_enumerate_dir (MetaTree                         *tree,
1867
			 const char                       *path,
1868
			 meta_tree_dir_enumerate_callback  callback,
1869
			 gpointer                          user_data)
1870
{
1871
  EnumDirData data;
1872
  GHashTable *children;
1873
  EnumDirChildInfo *info;
1874
  MetaFileDirEnt *dirent;
1875
  GHashTableIter iter;
1876
  MetaFileDir *dir;
1877
  char *res_path;
1878
1879
  g_static_rw_lock_reader_lock (&metatree_lock);
1880
1881
  data.children = children =
1882
    g_hash_table_new_full (g_str_hash,
1883
			   g_str_equal,
1884
			   NULL,
1885
			   (GDestroyNotify)child_info_free);
1886
1887
1888
  res_path = meta_journal_iterate (tree->journal,
1889
				   path,
1890
				   enum_dir_iter_key,
1891
				   enum_dir_iter_path,
1892
				   &data);
1893
1894
  if (res_path != NULL)
1895
    {
1896
      dirent = meta_tree_lookup (tree, res_path);
1897
      if (dirent != NULL &&
1898
	  dirent->children != 0)
1899
	{
1900
	  dir = verify_children_block (tree, dirent->children);
1901
	  if (dir)
1902
	    {
1903
	      if (!enumerate_dir (tree, dir, children, callback, user_data))
1904
		goto out;
1905
	    }
1906
	}
1907
    }
1908
1909
  g_hash_table_iter_init (&iter, children);
1910
  while (g_hash_table_iter_next (&iter, NULL, (gpointer  *)&info))
1911
    {
1912
      if (info->reported || !info->exists)
1913
	continue;
1914
1915
      if (!callback (info->name,
1916
		     info->last_changed,
1917
		     info->has_children,
1918
		     info->has_data,
1919
		     user_data))
1920
	break;
1921
    }
1922
 out:
1.1.46 by Sebastien Bacher
Import upstream version 1.3.4
1923
  g_free (res_path);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
1924
  g_hash_table_destroy (children);
1925
  g_static_rw_lock_reader_unlock (&metatree_lock);
1926
}
1927
1928
typedef struct {
1929
  char *key;
1930
1931
  MetaKeyType type;
1932
  gpointer value;
1933
1934
  gboolean seen; /* We saw this key in the journal */
1935
} EnumKeysInfo;
1936
1937
typedef struct {
1938
  GHashTable *keys;
1939
} EnumKeysData;
1940
1941
1942
static void
1943
key_info_free (EnumKeysInfo *info)
1944
{
1945
  g_free (info->key);
1946
  g_free (info);
1947
}
1948
1949
static EnumKeysInfo *
1950
get_key_info (EnumKeysData *data,
1951
	      const char *key)
1952
{
1953
  EnumKeysInfo *info;
1954
1955
  info = g_hash_table_lookup (data->keys, key);
1956
  if (info == NULL)
1957
    {
1958
      info = g_new0 (EnumKeysInfo, 1);
1959
      info->key = g_strdup (key);
1960
      g_hash_table_insert (data->keys, info->key, info);
1961
    }
1962
1963
  return info;
1964
}
1965
1966
static gboolean
1967
enum_keys_iter_key (MetaJournal *journal,
1968
		    MetaJournalEntryType entry_type,
1969
		    const char *path,
1970
		    guint64 mtime,
1971
		    const char *key,
1972
		    gpointer value,
1973
		    char **iter_path,
1974
		    gpointer user_data)
1975
{
1976
  EnumKeysData *data = user_data;
1977
  EnumKeysInfo *info;
1978
1979
  if (strcmp (path, *iter_path) == 0)
1980
    {
1981
      info = get_key_info (data, key);
1982
1983
      if (!info->seen)
1984
	{
1985
	  info->seen = TRUE;
1986
	  if (entry_type == JOURNAL_OP_UNSET_KEY)
1987
	    info->type = META_KEY_TYPE_NONE;
1988
	  else if (entry_type == JOURNAL_OP_SET_KEY)
1989
	    info->type = META_KEY_TYPE_STRING;
1990
	  else
1991
	    info->type = META_KEY_TYPE_STRINGV;
1992
	  info->value = value;
1993
	}
1994
    }
1995
1996
  return TRUE; /* continue */
1997
}
1998
1999
static gboolean
2000
enum_keys_iter_path (MetaJournal *journal,
2001
		     MetaJournalEntryType entry_type,
2002
		     const char *path,
2003
		     guint64 mtime,
2004
		     const char *source_path,
2005
		     char **iter_path,
2006
		     gpointer user_data)
2007
{
2008
  const char *remainder;
2009
  char *old_path;
2010
2011
  /* is this a parent of the iter path */
2012
  remainder = get_prefix_match (*iter_path, path);
2013
  if (remainder != NULL)
2014
    {
2015
      /* path is affected as a child of this node */
2016
      if (entry_type == JOURNAL_OP_REMOVE_PATH)
2017
	return FALSE; /* stop iterating */
2018
      else if (entry_type == JOURNAL_OP_COPY_PATH)
2019
	{
2020
	  old_path = *iter_path;
2021
	  *iter_path = g_build_filename (source_path, remainder, NULL);
2022
	  g_free (old_path);
2023
	  return TRUE; /* Continue, with new path */
2024
	}
2025
    }
2026
2027
  return TRUE;
2028
}
2029
2030
static gboolean
2031
enumerate_data (MetaTree *tree,
2032
		MetaFileData *data,
2033
		GHashTable *keys,
2034
		meta_tree_keys_enumerate_callback callback,
2035
		gpointer user_data)
2036
{
2037
  guint32 i, j, num_keys, num_strings;
2038
  MetaFileDataEnt *ent;
2039
  EnumKeysInfo *info;
2040
  char *key_name;
2041
  guint32 key_id;
2042
  MetaKeyType type;
2043
  gpointer value;
2044
  MetaFileStringv *stringv;
2045
  gpointer free_me;
2046
  char **strv;
2047
  char *strv_static[10];
2048
2049
  num_keys = GUINT32_FROM_BE (data->num_keys);
2050
  for (i = 0; i < num_keys; i++)
2051
    {
2052
      ent = &data->keys[i];
2053
2054
      key_id = GUINT32_FROM_BE (ent->key) & ~KEY_IS_LIST_MASK;
2055
      if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK)
2056
	type = META_KEY_TYPE_STRINGV;
2057
      else
2058
	type = META_KEY_TYPE_STRING;
2059
2060
      if (key_id >= tree->num_attributes)
2061
	continue;
2062
2063
      key_name = tree->attributes[key_id];
2064
      if (key_name == NULL)
2065
	continue;
2066
2067
      info = g_hash_table_lookup (keys, key_name);
2068
      if (info)
2069
	continue; /* overridden, handle later */
2070
2071
      free_me = NULL;
2072
      if (type == META_KEY_TYPE_STRING)
2073
	value = verify_string (tree, ent->value);
2074
      else
2075
	{
2076
	  stringv = verify_array_block (tree, ent->value,
2077
					sizeof (guint32));
2078
	  num_strings = GUINT32_FROM_BE (stringv->num_strings);
2079
2080
	  if (num_strings < 10)
2081
	    strv = strv_static;
2082
	  else
2083
	    {
2084
	      strv = g_new (char *, num_strings + 1);
2085
	      free_me = (gpointer)strv;
2086
	    }
2087
2088
	  for (j = 0; j < num_strings; j++)
2089
	    strv[j] = verify_string (tree, stringv->strings[j]);
2090
	  strv[j] = NULL;
2091
2092
	  value = strv;
2093
	}
2094
2095
      if (!callback (key_name,
2096
		     type,
2097
		     value,
2098
		     user_data))
2099
	return FALSE;
2100
2101
      g_free (free_me);
2102
    }
2103
  return TRUE;
2104
}
2105
2106
void
2107
meta_tree_enumerate_keys (MetaTree                         *tree,
2108
			  const char                       *path,
2109
			  meta_tree_keys_enumerate_callback callback,
2110
			  gpointer                          user_data)
2111
{
2112
  EnumKeysData keydata;
2113
  GHashTable *keys;
2114
  EnumKeysInfo *info;
2115
  MetaFileData *data;
2116
  GHashTableIter iter;
2117
  char *res_path;
2118
2119
  g_static_rw_lock_reader_lock (&metatree_lock);
2120
2121
  keydata.keys = keys =
2122
    g_hash_table_new_full (g_str_hash,
2123
			   g_str_equal,
2124
			   NULL,
2125
			   (GDestroyNotify)key_info_free);
2126
2127
2128
  res_path = meta_journal_iterate (tree->journal,
2129
				   path,
2130
				   enum_keys_iter_key,
2131
				   enum_keys_iter_path,
2132
				   &keydata);
2133
2134
  if (res_path != NULL)
2135
    {
2136
      data = meta_tree_lookup_data (tree, res_path);
2137
      if (data != NULL)
2138
	{
2139
	  if (!enumerate_data (tree, data, keys, callback, user_data))
2140
	    goto out;
2141
	}
2142
    }
2143
2144
  g_hash_table_iter_init (&iter, keys);
2145
  while (g_hash_table_iter_next (&iter, NULL, (gpointer  *)&info))
2146
    {
2147
      gpointer value;
2148
2149
      if (info->type == META_KEY_TYPE_NONE)
2150
	continue;
2151
2152
      if (info->type == META_KEY_TYPE_STRING)
2153
	value = info->value;
2154
      else
2155
	{
2156
	  g_assert (info->type == META_KEY_TYPE_STRINGV);
2157
	  value = get_stringv_from_journal (info->value, FALSE);
2158
	}
2159
2160
      if (!callback (info->key,
2161
		     info->type,
2162
		     value,
2163
		     user_data))
2164
	break;
2165
2166
      if (info->type == META_KEY_TYPE_STRINGV)
2167
	g_free (value);
2168
2169
    }
2170
 out:
1.1.46 by Sebastien Bacher
Import upstream version 1.3.4
2171
  g_free (res_path);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
2172
  g_hash_table_destroy (keys);
2173
  g_static_rw_lock_reader_unlock (&metatree_lock);
2174
}
2175
2176
2177
static void
2178
copy_tree_to_builder (MetaTree *tree,
2179
		      MetaFileDirEnt *dirent,
2180
		      MetaFile *builder_file)
2181
{
2182
  MetaFile *builder_child;
2183
  MetaFileData *data;
2184
  MetaFileDataEnt *ent;
2185
  MetaFileDir *dir;
2186
  MetaFileDirEnt *child_dirent;
2187
  MetaKeyType type;
2188
  char *child_name, *key_name, *value;
2189
  guint32 i, num_keys, num_children, j;
2190
  guint32 key_id;
2191
2192
  /* Copy metadata */
2193
  data = verify_metadata_block (tree, dirent->metadata);
2194
  if (data)
2195
    {
2196
      num_keys = GUINT32_FROM_BE (data->num_keys);
2197
      for (i = 0; i < num_keys; i++)
2198
	{
2199
	  ent = &data->keys[i];
2200
2201
	  key_id = GUINT32_FROM_BE (ent->key) & ~KEY_IS_LIST_MASK;
2202
	  if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK)
2203
	    type = META_KEY_TYPE_STRINGV;
2204
	  else
2205
	    type = META_KEY_TYPE_STRING;
2206
2207
	  if (key_id >= tree->num_attributes)
2208
	    continue;
2209
2210
	  key_name = tree->attributes[key_id];
2211
	  if (key_name == NULL)
2212
	    continue;
2213
2214
	  if (type == META_KEY_TYPE_STRING)
2215
	    {
2216
	      value = verify_string (tree, ent->value);
2217
	      if (value)
2218
		metafile_key_set_value (builder_file,
2219
					key_name, value);
2220
	    }
2221
	  else
2222
	    {
2223
	      MetaFileStringv *stringv;
2224
	      guint32 num_strings;
2225
	      char *str;
2226
2227
	      stringv = verify_array_block (tree, ent->value,
2228
					    sizeof (guint32));
2229
2230
	      if (stringv)
2231
		{
2232
		  metafile_key_list_set (builder_file, key_name);
2233
2234
		  num_strings = GUINT32_FROM_BE (stringv->num_strings);
2235
		  for (j = 0; j < num_strings; j++)
2236
		    {
2237
		      str = verify_string (tree, stringv->strings[j]);
2238
		      if (str)
2239
			metafile_key_list_add (builder_file,
2240
					       key_name, str);
2241
		    }
2242
		}
2243
	    }
2244
	}
2245
    }
2246
2247
  /* Copy last changed time */
2248
  builder_file->last_changed = get_time_t (tree, dirent->last_changed);
2249
2250
  /* Copy children */
2251
  if (dirent->children != 0 &&
2252
      (dir = verify_children_block (tree, dirent->children)) != NULL)
2253
    {
2254
      num_children = GUINT32_FROM_BE (dir->num_children);
2255
      for (i = 0; i < num_children; i++)
2256
	{
2257
	  child_dirent = &dir->children[i];
2258
	  child_name = verify_string (tree, child_dirent->name);
2259
	  if (child_name != NULL)
2260
	    {
2261
	      builder_child = metafile_new (child_name, builder_file);
2262
	      copy_tree_to_builder (tree, child_dirent, builder_child);
2263
	    }
2264
	}
2265
    }
2266
}
2267
2268
static void
2269
apply_journal_to_builder (MetaTree *tree,
2270
			  MetaBuilder *builder)
2271
{
2272
  MetaJournal *journal;
2273
  MetaJournalEntry *entry;
2274
  guint32 *sizep;
2275
  guint64 mtime;
2276
  char *journal_path, *journal_key, *source_path;
2277
  char *value;
2278
  char **strv;
2279
  MetaFile *file;
2280
  int i;
2281
2282
  journal = tree->journal;
2283
2284
  entry = journal->first_entry;
2285
  while (entry < journal->last_entry)
2286
    {
2287
      mtime = GUINT64_FROM_BE (entry->mtime);
2288
      journal_path = &entry->path[0];
2289
2290
      switch (entry->entry_type)
2291
	{
2292
	case JOURNAL_OP_SET_KEY:
2293
	  journal_key = get_next_arg (journal_path);
2294
	  value = get_next_arg (journal_key);
2295
	  file = meta_builder_lookup (builder, journal_path, TRUE);
2296
	  metafile_key_set_value (file,
2297
				  journal_key,
2298
				  value);
2299
	  metafile_set_mtime (file, mtime);
2300
	  break;
2301
	case JOURNAL_OP_SETV_KEY:
2302
	  journal_key = get_next_arg (journal_path);
2303
	  value = get_next_arg (journal_key);
2304
	  strv = get_stringv_from_journal (value, FALSE);
2305
	  file = meta_builder_lookup (builder, journal_path, TRUE);
2306
2307
	  metafile_key_list_set (file, journal_key);
2308
	  for (i = 0; strv[i] != NULL; i++)
2309
	    metafile_key_list_add (file, journal_key, strv[i]);
2310
2311
	  g_free (strv);
2312
	  metafile_set_mtime (file, mtime);
2313
	  break;
2314
	case JOURNAL_OP_UNSET_KEY:
2315
	  journal_key = get_next_arg (journal_path);
2316
	  file = meta_builder_lookup (builder, journal_path, FALSE);
2317
	  if (file)
2318
	    {
2319
	      metafile_key_unset (file, journal_key);
2320
	      metafile_set_mtime (file, mtime);
2321
	    }
2322
	  break;
2323
	case JOURNAL_OP_COPY_PATH:
2324
	  source_path = get_next_arg (journal_path);
2325
	  meta_builder_copy (builder,
2326
			     source_path,
2327
			     journal_path,
2328
			     mtime);
2329
	  break;
2330
	case JOURNAL_OP_REMOVE_PATH:
2331
	  meta_builder_remove (builder,
2332
			       journal_path,
2333
			       mtime);
2334
	  break;
2335
	default:
2336
	  break;
2337
	}
2338
2339
      sizep = (guint32 *)entry;
2340
      entry = (MetaJournalEntry *)((char *)entry + GUINT32_FROM_BE (*(sizep)));
2341
    }
2342
}
2343
2344
2345
/* Needs write lock */
2346
static gboolean
2347
meta_tree_flush_locked (MetaTree *tree)
2348
{
2349
  MetaBuilder *builder;
1.1.47 by Martin Pitt
Import upstream version 1.3.5
2350
  gboolean res;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
2351
2352
  builder = meta_builder_new ();
2353
2354
  copy_tree_to_builder (tree, tree->root, builder->root);
2355
2356
  if (tree->journal)
2357
    apply_journal_to_builder (tree, builder);
2358
1.1.47 by Martin Pitt
Import upstream version 1.3.5
2359
  res = meta_builder_write (builder,
2360
			    meta_tree_get_filename (tree));
2361
  if (res)
2362
    meta_tree_refresh_locked (tree);
2363
2364
  meta_builder_free (builder);
2365
2366
  return res;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
2367
}
2368
2369
gboolean
2370
meta_tree_flush (MetaTree *tree)
2371
{
2372
  gboolean res;
2373
2374
  g_static_rw_lock_writer_lock (&metatree_lock);
2375
  res = meta_tree_flush_locked (tree);
2376
  g_static_rw_lock_writer_unlock (&metatree_lock);
2377
  return res;
2378
}
2379
2380
gboolean
2381
meta_tree_unset (MetaTree                         *tree,
2382
		 const char                       *path,
2383
		 const char                       *key)
2384
{
2385
  GString *entry;
2386
  guint64 mtime;
2387
  gboolean res;
2388
2389
  g_static_rw_lock_writer_lock (&metatree_lock);
2390
2391
  if (tree->journal == NULL ||
2392
      !tree->journal->journal_valid)
2393
    {
2394
      res = FALSE;
2395
      goto out;
2396
    }
2397
2398
  mtime = time (NULL);
2399
2400
  entry = meta_journal_entry_new_unset (mtime, path, key);
2401
2402
  res = TRUE;
2403
 retry:
2404
  if (!meta_journal_add_entry (tree->journal, entry))
2405
    {
2406
      if (meta_tree_flush_locked (tree))
2407
	goto retry;
2408
2409
      res = FALSE;
2410
    }
2411
2412
  g_string_free (entry, TRUE);
2413
2414
 out:
2415
  g_static_rw_lock_writer_unlock (&metatree_lock);
2416
  return res;
2417
}
2418
2419
gboolean
2420
meta_tree_set_string (MetaTree                         *tree,
2421
		      const char                       *path,
2422
		      const char                       *key,
2423
		      const char                       *value)
2424
{
2425
  GString *entry;
2426
  guint64 mtime;
2427
  gboolean res;
2428
2429
  g_static_rw_lock_writer_lock (&metatree_lock);
2430
2431
  if (tree->journal == NULL ||
2432
      !tree->journal->journal_valid)
2433
    {
2434
      res = FALSE;
2435
      goto out;
2436
    }
2437
2438
  mtime = time (NULL);
2439
2440
  entry = meta_journal_entry_new_set (mtime, path, key, value);
2441
2442
  res = TRUE;
2443
 retry:
2444
  if (!meta_journal_add_entry (tree->journal, entry))
2445
    {
2446
      if (meta_tree_flush_locked (tree))
2447
	goto retry;
2448
2449
      res = FALSE;
2450
    }
2451
2452
  g_string_free (entry, TRUE);
2453
2454
 out:
2455
  g_static_rw_lock_writer_unlock (&metatree_lock);
2456
  return res;
2457
}
2458
2459
gboolean
2460
meta_tree_set_stringv (MetaTree                         *tree,
2461
		       const char                       *path,
2462
		       const char                       *key,
2463
		       char                            **value)
2464
{
2465
  GString *entry;
2466
  guint64 mtime;
2467
  gboolean res;
2468
2469
  g_static_rw_lock_writer_lock (&metatree_lock);
2470
2471
  if (tree->journal == NULL ||
2472
      !tree->journal->journal_valid)
2473
    {
2474
      res = FALSE;
2475
      goto out;
2476
    }
2477
2478
  mtime = time (NULL);
2479
2480
  entry = meta_journal_entry_new_setv (mtime, path, key, value);
2481
2482
  res = TRUE;
2483
 retry:
2484
  if (!meta_journal_add_entry (tree->journal, entry))
2485
    {
2486
      if (meta_tree_flush_locked (tree))
2487
	goto retry;
2488
2489
      res = FALSE;
2490
    }
2491
2492
  g_string_free (entry, TRUE);
2493
2494
 out:
2495
  g_static_rw_lock_writer_unlock (&metatree_lock);
2496
  return res;
2497
}
2498
2499
gboolean
2500
meta_tree_remove (MetaTree *tree,
2501
		  const char *path)
2502
{
2503
  GString *entry;
2504
  guint64 mtime;
2505
  gboolean res;
2506
2507
  g_static_rw_lock_writer_lock (&metatree_lock);
2508
2509
  if (tree->journal == NULL ||
2510
      !tree->journal->journal_valid)
2511
    {
2512
      res = FALSE;
2513
      goto out;
2514
    }
2515
2516
  mtime = time (NULL);
2517
2518
  entry = meta_journal_entry_new_remove (mtime, path);
2519
2520
  res = TRUE;
2521
 retry:
2522
  if (!meta_journal_add_entry (tree->journal, entry))
2523
    {
2524
      if (meta_tree_flush_locked (tree))
2525
	goto retry;
2526
2527
      res = FALSE;
2528
    }
2529
2530
  g_string_free (entry, TRUE);
2531
2532
 out:
2533
  g_static_rw_lock_writer_unlock (&metatree_lock);
2534
  return res;
2535
}
2536
2537
gboolean
2538
meta_tree_copy (MetaTree                         *tree,
2539
		const char                       *src,
2540
		const char                       *dest)
2541
{
2542
  GString *entry;
2543
  guint64 mtime;
2544
  gboolean res;
2545
2546
  g_static_rw_lock_writer_lock (&metatree_lock);
2547
2548
  if (tree->journal == NULL ||
2549
      !tree->journal->journal_valid)
2550
    {
2551
      res = FALSE;
2552
      goto out;
2553
    }
2554
2555
  mtime = time (NULL);
2556
2557
  entry = meta_journal_entry_new_copy (mtime, src, dest);
2558
2559
  res = TRUE;
2560
 retry:
2561
  if (!meta_journal_add_entry (tree->journal, entry))
2562
    {
2563
      if (meta_tree_flush_locked (tree))
2564
	goto retry;
2565
2566
      res = FALSE;
2567
    }
2568
2569
  g_string_free (entry, TRUE);
2570
2571
 out:
2572
  g_static_rw_lock_writer_unlock (&metatree_lock);
2573
  return res;
2574
}
2575
2576
static char *
2577
canonicalize_filename (const char *filename)
2578
{
2579
  char *canon, *start, *p, *q;
2580
  char *cwd;
2581
  int i;
2582
2583
  if (!g_path_is_absolute (filename))
2584
    {
2585
      cwd = g_get_current_dir ();
2586
      canon = g_build_filename (cwd, filename, NULL);
2587
      g_free (cwd);
2588
    }
2589
  else
2590
    canon = g_strdup (filename);
2591
2592
  start = (char *)g_path_skip_root (canon);
2593
2594
  if (start == NULL)
2595
    {
2596
      /* This shouldn't really happen, as g_get_current_dir() should
2597
	 return an absolute pathname, but bug 573843 shows this is
2598
	 not always happening */
2599
      g_free (canon);
2600
      return g_build_filename (G_DIR_SEPARATOR_S, filename, NULL);
2601
    }
2602
2603
  /* POSIX allows double slashes at the start to
2604
   * mean something special (as does windows too).
2605
   * So, "//" != "/", but more than two slashes
2606
   * is treated as "/".
2607
   */
2608
  i = 0;
2609
  for (p = start - 1;
2610
       (p >= canon) &&
2611
	 G_IS_DIR_SEPARATOR (*p);
2612
       p--)
2613
    i++;
2614
  if (i > 2)
2615
    {
2616
      i -= 1;
2617
      start -= i;
2618
      memmove (start, start+i, strlen (start+i)+1);
2619
    }
2620
2621
  p = start;
2622
  while (*p != 0)
2623
    {
2624
      if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR (p[1])))
2625
	{
2626
	  memmove (p, p+1, strlen (p+1)+1);
2627
	}
2628
      else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || G_IS_DIR_SEPARATOR (p[2])))
2629
	{
2630
	  q = p + 2;
2631
	  /* Skip previous separator */
2632
	  p = p - 2;
2633
	  if (p < start)
2634
	    p = start;
2635
	  while (p > start && !G_IS_DIR_SEPARATOR (*p))
2636
	    p--;
2637
	  if (G_IS_DIR_SEPARATOR (*p))
2638
	    *p++ = G_DIR_SEPARATOR;
2639
	  memmove (p, q, strlen (q)+1);
2640
	}
2641
      else
2642
	{
2643
	  /* Skip until next separator */
2644
	  while (*p != 0 && !G_IS_DIR_SEPARATOR (*p))
2645
	    p++;
2646
2647
	  if (*p != 0)
2648
	    {
2649
	      /* Canonicalize one separator */
2650
	      *p++ = G_DIR_SEPARATOR;
2651
	    }
2652
	}
2653
2654
      /* Remove additional separators */
2655
      q = p;
2656
      while (*q && G_IS_DIR_SEPARATOR (*q))
2657
	q++;
2658
2659
      if (p != q)
2660
	memmove (p, q, strlen (q)+1);
2661
    }
2662
2663
  /* Remove trailing slashes */
2664
  if (p > start && G_IS_DIR_SEPARATOR (*(p-1)))
2665
    *(p-1) = 0;
2666
2667
  return canon;
2668
}
2669
2670
/* Invariant: If link is canonical, so is result */
2671
static char *
2672
follow_symlink (const char *link)
2673
{
2674
  char *resolved, *canonical, *parent;
2675
  char symlink_value[4096];
2676
  ssize_t res;
2677
2678
  res = readlink (link, symlink_value, sizeof (symlink_value) - 1);
2679
2680
  if (res == -1)
2681
    return g_strdup (link);
2682
  symlink_value[res] = 0;
2683
2684
  if (g_path_is_absolute (symlink_value))
2685
    return canonicalize_filename (symlink_value);
2686
  else
2687
    {
2688
      parent = g_path_get_dirname (link);
2689
      resolved = g_build_filename (parent, symlink_value, NULL);
2690
      g_free (parent);
2691
2692
      canonical = canonicalize_filename (resolved);
2693
2694
      g_free (resolved);
2695
2696
      return canonical;
2697
    }
2698
}
2699
2700
/* Returns parent path or NULL if none */
2701
static char *
2702
get_dirname (const char *path)
2703
{
2704
  char *parent;
2705
2706
  parent = g_path_get_dirname (path);
2707
  if (strcmp (parent, ".") == 0 ||
2708
      strcmp (parent, path) == 0)
2709
    {
2710
      g_free (parent);
2711
      return NULL;
2712
    }
2713
2714
  return parent;
2715
}
2716
2717
/* Invariant: If path is canonical, so is result */
2718
static void
2719
follow_symlink_recursively (char **path,
2720
			    dev_t *path_dev)
2721
{
2722
  char *tmp;
2723
  struct stat path_stat;
2724
  int num_recursions;
2725
2726
  num_recursions = 0;
2727
  do {
2728
    if (g_lstat (*path, &path_stat) != 0)
2729
      {
2730
	*path_dev = 0;
2731
	return;
2732
      }
2733
2734
    if (S_ISLNK (path_stat.st_mode))
2735
      {
2736
	tmp = *path;
2737
	*path = follow_symlink (*path);
2738
	g_free (tmp);
2739
      }
2740
2741
    num_recursions++;
2742
    if (num_recursions > 12)
2743
      break;
2744
  } while (S_ISLNK (path_stat.st_mode));
2745
2746
  *path_dev = path_stat.st_dev;
2747
}
2748
2749
struct _MetaLookupCache {
2750
  char *last_parent;
2751
  char *last_parent_expanded;
2752
  dev_t last_parent_dev;
2753
  char *last_parent_mountpoint;
2754
  char *last_parent_mountpoint_extra_prefix;
2755
2756
  dev_t last_device;
2757
  char *last_device_tree;
2758
};
2759
2760
#ifdef HAVE_LIBUDEV
2761
154 by Sebastien Bacher
Correctly update using merge-upstream otherwise the diff will be reverted
2762
static struct udev *udev;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
2763
G_LOCK_DEFINE_STATIC (udev);
2764
2765
static char *
2766
get_tree_from_udev (MetaLookupCache *cache,
2767
		    dev_t devnum)
2768
{
2769
  struct udev_device *dev;
2770
  const char *uuid, *label;
2771
  char *res;
2772
2773
  G_LOCK (udev);
2774
2775
  if (udev == NULL)
2776
    udev = udev_new ();
2777
2778
  dev = udev_device_new_from_devnum (udev, 'b', devnum);
2779
  uuid = udev_device_get_property_value (dev, "ID_FS_UUID_ENC");
2780
2781
  res = NULL;
2782
  if (uuid)
2783
    {
2784
      res = g_strconcat ("uuid-", uuid, NULL);
2785
    }
2786
  else
2787
    {
2788
      label = udev_device_get_property_value (dev, "ID_FS_LABEL_ENC");
2789
2790
      if (label)
2791
	res = g_strconcat ("label-", label, NULL);
2792
    }
2793
2794
  udev_device_unref (dev);
2795
2796
  G_UNLOCK (udev);
2797
2798
  return res;
2799
}
2800
#endif
2801
2802
static const char *
2803
get_tree_for_device (MetaLookupCache *cache,
2804
		     dev_t device)
2805
{
2806
#ifdef HAVE_LIBUDEV
2807
  if (device != cache->last_device)
2808
    {
2809
      cache->last_device = device;
2810
      g_free (cache->last_device_tree);
2811
      cache->last_device_tree = get_tree_from_udev (cache, device);
2812
    }
2813
2814
  return cache->last_device_tree;
2815
#endif
2816
  return NULL;
2817
}
2818
2819
2820
#ifdef __linux__
2821
2822
typedef struct {
2823
  char *mountpoint;
2824
  char *root;
2825
} MountinfoEntry;
2826
2827
static gboolean mountinfo_initialized = FALSE;
2828
static int mountinfo_fd = -1;
2829
static MountinfoEntry *mountinfo_roots = NULL;
2830
G_LOCK_DEFINE_STATIC (mountinfo);
2831
2832
/* We want to avoid mmap and stat as these are not ideal
2833
   operations for a proc file */
2834
static char *
2835
read_contents (int fd)
2836
{
2837
  char *data;
2838
  gsize len;
2839
  gsize bytes_read;
2840
2841
  len = 4096;
2842
  data = g_malloc (len);
2843
2844
  bytes_read = 0;
2845
  while (1)
2846
    {
2847
      gssize rc;
2848
2849
      if (len - bytes_read < 100)
2850
	{
2851
	  len = len + 4096;
2852
	  data = g_realloc (data, len);
2853
	}
2854
2855
      rc = read (fd, data + bytes_read,
2856
		 len - bytes_read);
2857
      if (rc < 0)
2858
	{
2859
	  if (errno != EINTR)
2860
	    {
2861
	      g_free (data);
2862
	      return NULL;
2863
	    }
2864
	}
2865
      else if (rc == 0)
2866
	break;
2867
      else
2868
	bytes_read += rc;
2869
    }
2870
2871
  /* zero terminate */
2872
  if (len - bytes_read < 1)
2873
    data = g_realloc (data, bytes_read + 1);
2874
  data[bytes_read] = 0;
2875
2876
  return (char *)data;
2877
}
2878
2879
static char *
2880
mountinfo_unescape (const char *escaped)
2881
{
2882
  char *res, *s;
2883
  char c;
2884
  gsize len;
2885
2886
  s = strchr (escaped, ' ');
2887
  if (s)
2888
    len = s - escaped;
2889
  else
2890
    len = strlen (escaped);
2891
  res = malloc (len + 1);
2892
  s = res;
2893
2894
  while (*escaped != 0 && *escaped != ' ')
2895
    {
2896
      if (*escaped == '\\')
2897
	{
2898
	  escaped++;
2899
	  c = *escaped++ - '0';
2900
	  c <<= 3;
2901
	  c |= *escaped++ - '0';
2902
	  c <<= 3;
2903
	  c |= *escaped++ - '0';
2904
	}
2905
      else
2906
	c = *escaped++;
2907
      *s++ = c;
2908
    }
2909
  *s = 0;
2910
  return res;
2911
}
2912
2913
static MountinfoEntry *
2914
parse_mountinfo (const char *contents)
2915
{
2916
  GArray *a;
2917
  const char *line;
2918
  const char *line_root;
2919
  const char *line_mountpoint;
2920
2921
  a = g_array_new (TRUE, TRUE, sizeof (MountinfoEntry));
2922
2923
  line = contents;
2924
  while (line != NULL && *line != 0)
2925
    {
2926
      /* parent id */
2927
      line = strchr (line, ' ');
2928
      line_mountpoint = NULL;
2929
      if (line)
2930
	{
2931
	  /* major:minor */
2932
	  line = strchr (line+1, ' ');
2933
	  if (line)
2934
	    {
2935
	      /* root */
2936
	      line = strchr (line+1, ' ');
2937
	      line_root = line + 1;
2938
	      if (line)
2939
		{
2940
		  /* mountpoint */
2941
		  line = strchr (line+1, ' ');
2942
		  line_mountpoint = line + 1;
2943
		}
2944
	    }
2945
	}
2946
2947
      if (line_mountpoint && !(line_root[0] == '/' && line_root[1] == ' '))
2948
	{
2949
	  MountinfoEntry new_entry;
2950
2951
	  new_entry.mountpoint = mountinfo_unescape (line_mountpoint);
2952
	  new_entry.root = mountinfo_unescape (line_root);
2953
2954
	  g_array_append_val (a, new_entry);
2955
	}
2956
1.1.72 by Martin Pitt
Import upstream version 1.9.3
2957
      if (line)
2958
	line = strchr (line, '\n');
1.1.44 by Robert Ancell
Import upstream version 1.3.2
2959
      if (line)
2960
	line++;
2961
    }
2962
2963
  return (MountinfoEntry *)g_array_free (a, FALSE);
2964
}
2965
2966
static void
2967
free_mountinfo (void)
2968
{
2969
  int i;
2970
2971
  if (mountinfo_roots)
2972
    {
2973
      for (i = 0; mountinfo_roots[i].mountpoint != NULL; i++)
2974
	{
2975
	  g_free (mountinfo_roots[i].mountpoint);
2976
	  g_free (mountinfo_roots[i].root);
2977
	}
2978
      g_free (mountinfo_roots);
2979
      mountinfo_roots = NULL;
2980
    }
2981
}
2982
2983
static void
2984
update_mountinfo (void)
2985
{
2986
  char *contents;
2987
  int res;
2988
  gboolean first;
1.1.54 by Martin Pitt
Import upstream version 1.5.4
2989
  GPollFD pfd;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
2990
2991
  first = FALSE;
2992
  if (!mountinfo_initialized)
2993
    {
2994
      mountinfo_initialized = TRUE;
2995
      mountinfo_fd = open ("/proc/self/mountinfo", O_RDONLY);
2996
      first = TRUE;
2997
    }
2998
2999
  if (mountinfo_fd == -1)
3000
    return;
3001
3002
  if (!first)
3003
    {
3004
      pfd.fd = mountinfo_fd;
1.1.54 by Martin Pitt
Import upstream version 1.5.4
3005
      pfd.events = G_IO_IN | G_IO_OUT | G_IO_PRI;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3006
      pfd.revents = 0;
1.1.54 by Martin Pitt
Import upstream version 1.5.4
3007
      res = g_poll (&pfd, 1, 0);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3008
      if (res == 0)
3009
	return;
3010
    }
3011
3012
  free_mountinfo ();
3013
  contents = read_contents (mountinfo_fd);
3014
  lseek (mountinfo_fd, SEEK_SET, 0);
3015
  if (contents)
1.1.49 by Sebastien Bacher
Import upstream version 1.4.0
3016
    {
3017
      mountinfo_roots = parse_mountinfo (contents);
3018
      g_free (contents);
3019
    }
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3020
}
3021
3022
static char *
3023
find_mountinfo_root_for_mountpoint (const char *mountpoint)
3024
{
3025
  char *res;
3026
  int i;
3027
3028
  res = NULL;
3029
3030
  G_LOCK (mountinfo);
3031
3032
  update_mountinfo ();
3033
3034
  if (mountinfo_roots)
3035
    {
3036
      for (i = 0; mountinfo_roots[i].mountpoint != NULL; i++)
3037
	{
3038
	  if (strcmp (mountinfo_roots[i].mountpoint, mountpoint) == 0)
3039
	    {
3040
	      res = g_strdup (mountinfo_roots[i].root);
3041
	      break;
3042
	    }
3043
	}
3044
    }
3045
3046
  G_UNLOCK (mountinfo);
3047
3048
  return res;
3049
}
3050
3051
#endif
3052
3053
3054
static char *
3055
get_extra_prefix_for_mount (const char *mountpoint)
3056
{
3057
#ifdef __linux__
3058
  return find_mountinfo_root_for_mountpoint (mountpoint);
3059
#endif
3060
  return NULL;
3061
}
3062
3063
static dev_t
3064
get_devnum (const char *path)
3065
{
3066
  struct stat path_stat;
3067
3068
  if (g_lstat (path, &path_stat) != 0)
3069
    return 0;
3070
3071
  return path_stat.st_dev;
3072
}
3073
3074
/* Expands symlinks for parents and look for a mountpoint
3075
 * file is symlink expanded and canonical
3076
 */
3077
static const char *
3078
find_mountpoint_for (MetaLookupCache *cache,
3079
		     const char *file,
3080
		     dev_t       dev,
3081
		     char      **prefix_out)
3082
{
3083
  char *first_dir, *dir, *last;
3084
  const char *prefix;
154 by Sebastien Bacher
Correctly update using merge-upstream otherwise the diff will be reverted
3085
  dev_t dir_dev = 0;
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3086
3087
  first_dir = get_dirname (file);
3088
  if (first_dir == NULL)
3089
    {
3090
      *prefix_out = g_strdup ("/");
3091
      return "/";
3092
    }
3093
3094
  g_assert (cache->last_parent_expanded != NULL);
3095
  g_assert (strcmp (cache->last_parent_expanded, first_dir) == 0);
3096
3097
  if (cache->last_parent_mountpoint != NULL)
3098
    goto out; /* Cache hit! */
3099
3100
  dir = g_strdup (first_dir);
3101
  last = g_strdup (file);
3102
  while (1)
3103
    {
1.1.49 by Sebastien Bacher
Import upstream version 1.4.0
3104
      if (dir)
3105
	dir_dev = get_devnum (dir);
154 by Sebastien Bacher
Correctly update using merge-upstream otherwise the diff will be reverted
3106
      if (dir == NULL || dev != dir_dev)
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3107
	{
3108
	  g_free (dir);
3109
	  cache->last_parent_mountpoint = last;
3110
	  cache->last_parent_mountpoint_extra_prefix = get_extra_prefix_for_mount (last);
3111
	  break;
3112
	}
3113
3114
      g_free (last);
3115
      last = dir;
3116
      dir = get_dirname (last);
3117
    }
3118
3119
 out:
3120
  g_free (first_dir);
3121
3122
  prefix = file + strlen (cache->last_parent_mountpoint);
3123
  if (*prefix == 0)
3124
    prefix = "/";
3125
3126
  if (cache->last_parent_mountpoint_extra_prefix)
3127
    *prefix_out = g_build_filename (cache->last_parent_mountpoint_extra_prefix, prefix, NULL);
3128
  else
3129
    *prefix_out = g_strdup (prefix);
3130
3131
  return cache->last_parent_mountpoint;
3132
}
3133
3134
/* Resolves all symlinks, including the ones for basename.
3135
 * Invariant: If path is canonical, so is result.
3136
 */
3137
static char *
3138
expand_all_symlinks (const char *path,
1.1.46 by Sebastien Bacher
Import upstream version 1.3.4
3139
		     dev_t      *dev_out)
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3140
{
3141
  char *parent, *parent_expanded;
3142
  char *basename, *res;
3143
  dev_t dev;
3144
  char *path_copy;
3145
3146
  path_copy = g_strdup (path);
3147
  follow_symlink_recursively (&path_copy, &dev);
3148
  if (dev_out)
3149
    *dev_out = dev;
3150
3151
  parent = get_dirname (path_copy);
3152
  if (parent)
3153
    {
3154
      parent_expanded = expand_all_symlinks (parent, NULL);
3155
      basename = g_path_get_basename (path_copy);
3156
      res = g_build_filename (parent_expanded, basename, NULL);
3157
      g_free (parent_expanded);
3158
      g_free (basename);
3159
      g_free (parent);
1.1.46 by Sebastien Bacher
Import upstream version 1.3.4
3160
      g_free (path_copy);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3161
    }
3162
  else
3163
    res = path_copy;
3164
3165
  return res;
3166
}
3167
3168
MetaLookupCache *
3169
meta_lookup_cache_new (void)
3170
{
3171
  MetaLookupCache *cache;
3172
3173
  cache = g_new0 (MetaLookupCache, 1);
3174
3175
  return cache;
3176
}
3177
3178
void
3179
meta_lookup_cache_free (MetaLookupCache *cache)
3180
{
3181
  g_free (cache->last_parent);
3182
  g_free (cache->last_parent_expanded);
3183
  g_free (cache->last_parent_mountpoint);
3184
  g_free (cache->last_parent_mountpoint_extra_prefix);
1.1.49 by Sebastien Bacher
Import upstream version 1.4.0
3185
  g_free (cache->last_device_tree);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3186
  g_free (cache);
3187
}
3188
3189
static gboolean
3190
path_has_prefix (const char *path,
3191
		 const char *prefix)
3192
{
3193
  int prefix_len;
3194
3195
  if (prefix == NULL)
3196
    return TRUE;
3197
3198
  prefix_len = strlen (prefix);
3199
3200
  if (strncmp (path, prefix, prefix_len) == 0 &&
3201
      (prefix_len == 0 || /* empty prefix always matches */
3202
       prefix[prefix_len - 1] == '/' || /* last char in prefix was a /, so it must be in path too */
3203
       path[prefix_len] == 0 ||
3204
       path[prefix_len] == '/'))
3205
    return TRUE;
3206
3207
  return FALSE;
3208
}
3209
3210
struct HomedirData {
3211
  dev_t device;
3212
  char *expanded_path;
3213
};
3214
3215
static char *
3216
expand_parents (MetaLookupCache *cache,
3217
		const char *path,
3218
		dev_t *parent_dev_out)
3219
{
3220
  char *parent;
3221
  char *basename, *res;
3222
  char *path_copy;
3223
  dev_t parent_dev;
3224
3225
  path_copy = canonicalize_filename (path);
3226
  parent = get_dirname (path_copy);
3227
  if (parent == NULL)
3228
    {
3229
      *parent_dev_out = 0;
3230
      return path_copy;
3231
    }
3232
3233
  if (cache->last_parent == NULL ||
3234
      strcmp (cache->last_parent, parent) != 0)
3235
    {
3236
      g_free (cache->last_parent);
1.1.49 by Sebastien Bacher
Import upstream version 1.4.0
3237
      g_free (cache->last_parent_expanded);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3238
      cache->last_parent = parent;
3239
      cache->last_parent_expanded = expand_all_symlinks (parent, &parent_dev);
3240
      cache->last_parent_dev = parent_dev;
3241
      g_free (cache->last_parent_mountpoint);
3242
      cache->last_parent_mountpoint = NULL;
3243
      g_free (cache->last_parent_mountpoint_extra_prefix);
3244
      cache->last_parent_mountpoint_extra_prefix = NULL;
3245
   }
3246
  else
3247
    g_free (parent);
3248
3249
  *parent_dev_out = cache->last_parent_dev;
3250
  basename = g_path_get_basename (path_copy);
1.1.49 by Sebastien Bacher
Import upstream version 1.4.0
3251
  g_free (path_copy);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3252
  res = g_build_filename (cache->last_parent_expanded, basename, NULL);
3253
  g_free (basename);
3254
3255
  return res;
3256
}
3257
3258
MetaTree *
3259
meta_lookup_cache_lookup_path (MetaLookupCache *cache,
3260
			       const char *filename,
3261
			       guint64 device,
3262
			       gboolean for_write,
3263
			       char **tree_path)
3264
{
3265
  const char *mountpoint;
3266
  const char *treename;
3267
  char *prefix;
3268
  char *expanded;
3269
  static struct HomedirData homedir_data_storage;
3270
  static gsize homedir_datap = 0;
3271
  struct HomedirData *homedir_data;
3272
  MetaTree *tree;
3273
  dev_t parent_dev;
3274
3275
  if (g_once_init_enter (&homedir_datap))
3276
    {
3277
      char *e;
3278
      struct stat statbuf;
3279
3280
      g_stat (g_get_home_dir(), &statbuf);
3281
      homedir_data_storage.device = statbuf.st_dev;
3282
      e = canonicalize_filename (g_get_home_dir());
3283
      homedir_data_storage.expanded_path = expand_all_symlinks (e, NULL);
3284
      g_free (e);
3285
      g_once_init_leave (&homedir_datap, (gsize)&homedir_data_storage);
3286
    }
3287
  homedir_data = (struct HomedirData *)homedir_datap;
3288
3289
  /* Canonicalized form with all symlinks expanded in parents */
3290
  expanded = expand_parents (cache, filename, &parent_dev);
3291
3292
  if (device == 0) /* Unknown, use same as parent */
3293
    device = parent_dev;
3294
3295
  if (homedir_data->device == device &&
3296
      path_has_prefix (expanded, homedir_data->expanded_path))
3297
    {
3298
      treename = "home";
3299
      prefix = expanded + strlen (homedir_data->expanded_path);
3300
      if (*prefix == 0)
3301
	prefix = g_strdup ("/");
3302
      else
3303
	prefix = g_strdup (prefix);
3304
      goto found;
3305
    }
3306
3307
  treename = get_tree_for_device (cache, device);
3308
3309
  if (treename)
3310
    {
3311
      mountpoint = find_mountpoint_for (cache,
3312
					expanded,
3313
					device,
3314
					&prefix);
3315
3316
      if (mountpoint == NULL ||
3317
	  strcmp (mountpoint, "/") == 0)
3318
	{
3319
	  /* Fall back to root */
3320
	  g_free (prefix);
3321
	  treename = NULL;
3322
	}
3323
    }
3324
3325
  if (!treename)
3326
    {
3327
      treename = "root";
3328
      prefix = g_strdup (expanded);
3329
    }
3330
3331
 found:
1.1.49 by Sebastien Bacher
Import upstream version 1.4.0
3332
  g_free (expanded);
1.1.44 by Robert Ancell
Import upstream version 1.3.2
3333
  tree = meta_tree_lookup_by_name (treename, for_write);
3334
  if (tree)
3335
    {
3336
      *tree_path = prefix;
3337
      return tree;
3338
    }
3339
3340
  g_free (prefix);
3341
  return NULL;
3342
}