3568595eda5542c3181466c98e924fb9c700a46d
[platform/upstream/fontconfig.git] / src / fccache.c
1 /*
2  * Copyright © 2000 Keith Packard
3  * Copyright © 2005 Patrick Lam
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation, and that the name of the author(s) not be used in
10  * advertising or publicity pertaining to distribution of the software without
11  * specific, written prior permission.  The authors make no
12  * representations about the suitability of this software for any purpose.  It
13  * is provided "as is" without express or implied warranty.
14  *
15  * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21  * PERFORMANCE OF THIS SOFTWARE.
22  */
23 #include "fcint.h"
24 #include "fcarch.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <assert.h>
32 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
33 #  include <unistd.h>
34 #  include <sys/mman.h>
35 #endif
36
37 #ifndef O_BINARY
38 #define O_BINARY 0
39 #endif
40
41
42 struct MD5Context {
43         FcChar32 buf[4];
44         FcChar32 bits[2];
45         unsigned char in[64];
46 };
47
48 static void MD5Init(struct MD5Context *ctx);
49 static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned len);
50 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx);
51 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]);
52
53 #define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
54
55 static FcBool
56 FcCacheIsMmapSafe (int fd)
57 {
58     enum {
59       MMAP_NOT_INITIALIZED = 0,
60       MMAP_USE,
61       MMAP_DONT_USE,
62       MMAP_CHECK_FS,
63     } status;
64     static void *static_status;
65
66     status = (intptr_t) fc_atomic_ptr_get (&static_status);
67
68     if (status == MMAP_NOT_INITIALIZED)
69     {
70         const char *env = getenv ("FONTCONFIG_USE_MMAP");
71         FcBool use;
72         if (env && FcNameBool ((const FcChar8 *) env, &use))
73             status =  use ? MMAP_USE : MMAP_DONT_USE;
74         else
75             status = MMAP_CHECK_FS;
76         (void) fc_atomic_ptr_cmpexch (&static_status, NULL, (void *) status);
77     }
78
79     if (status == MMAP_CHECK_FS)
80         return FcIsFsMmapSafe (fd);
81     else
82         return status == MMAP_USE;
83
84 }
85
86 static const char bin2hex[] = { '0', '1', '2', '3',
87                                 '4', '5', '6', '7',
88                                 '8', '9', 'a', 'b',
89                                 'c', 'd', 'e', 'f' };
90
91 static FcChar8 *
92 FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
93 {
94     unsigned char       hash[16];
95     FcChar8             *hex_hash;
96     int                 cnt;
97     struct MD5Context   ctx;
98
99     MD5Init (&ctx);
100     MD5Update (&ctx, (const unsigned char *)dir, strlen ((const char *) dir));
101
102     MD5Final (hash, &ctx);
103
104     cache_base[0] = '/';
105     hex_hash = cache_base + 1;
106     for (cnt = 0; cnt < 16; ++cnt)
107     {
108         hex_hash[2*cnt  ] = bin2hex[hash[cnt] >> 4];
109         hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
110     }
111     hex_hash[2*cnt] = 0;
112     strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
113
114     return cache_base;
115 }
116
117 FcBool
118 FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
119 {
120     FcChar8     *cache_hashed = NULL;
121     FcChar8     cache_base[CACHEBASE_LEN];
122     FcStrList   *list;
123     FcChar8     *cache_dir;
124     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
125
126     FcDirCacheBasename (dir, cache_base);
127
128     list = FcStrListCreate (config->cacheDirs);
129     if (!list)
130         return FcFalse;
131         
132     while ((cache_dir = FcStrListNext (list)))
133     {
134         if (sysroot)
135             cache_hashed = FcStrBuildFilename (sysroot, cache_dir, cache_base, NULL);
136         else
137             cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
138         if (!cache_hashed)
139             break;
140         (void) unlink ((char *) cache_hashed);
141         FcStrFree (cache_hashed);
142     }
143     FcStrListDone (list);
144     /* return FcFalse if something went wrong */
145     if (cache_dir)
146         return FcFalse;
147     return FcTrue;
148 }
149
150 static int
151 FcDirCacheOpenFile (const FcChar8 *cache_file, struct stat *file_stat)
152 {
153     int fd;
154
155 #ifdef _WIN32
156     if (FcStat (cache_file, file_stat) < 0)
157         return -1;
158 #endif
159     fd = FcOpen((char *) cache_file, O_RDONLY | O_BINARY);
160     if (fd < 0)
161         return fd;
162 #ifndef _WIN32
163     if (fstat (fd, file_stat) < 0)
164     {
165         close (fd);
166         return -1;
167     }
168 #endif
169     return fd;
170 }
171
172 /*
173  * Look for a cache file for the specified dir. Attempt
174  * to use each one we find, stopping when the callback
175  * indicates success
176  */
177 static FcBool
178 FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
179                    FcBool (*callback) (int fd, struct stat *fd_stat,
180                                        struct stat *dir_stat, void *closure),
181                    void *closure, FcChar8 **cache_file_ret)
182 {
183     int         fd = -1;
184     FcChar8     cache_base[CACHEBASE_LEN];
185     FcStrList   *list;
186     FcChar8     *cache_dir;
187     struct stat file_stat, dir_stat;
188     FcBool      ret = FcFalse;
189
190     if (FcStatChecksum (dir, &dir_stat) < 0)
191         return FcFalse;
192
193     FcDirCacheBasename (dir, cache_base);
194
195     list = FcStrListCreate (config->cacheDirs);
196     if (!list)
197         return FcFalse;
198         
199     while ((cache_dir = FcStrListNext (list)))
200     {
201         const FcChar8 *sysroot = FcConfigGetSysRoot (config);
202         FcChar8 *cache_hashed;
203
204         if (sysroot)
205             cache_hashed = FcStrBuildFilename (sysroot, cache_dir, cache_base, NULL);
206         else
207             cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
208         if (!cache_hashed)
209             break;
210         fd = FcDirCacheOpenFile (cache_hashed, &file_stat);
211         if (fd >= 0) {
212             ret = (*callback) (fd, &file_stat, &dir_stat, closure);
213             close (fd);
214             if (ret)
215             {
216                 if (cache_file_ret)
217                     *cache_file_ret = cache_hashed;
218                 else
219                     FcStrFree (cache_hashed);
220                 break;
221             }
222         }
223         FcStrFree (cache_hashed);
224     }
225     FcStrListDone (list);
226
227     return ret;
228 }
229
230 #define FC_CACHE_MIN_MMAP   1024
231
232 /*
233  * Skip list element, make sure the 'next' pointer is the last thing
234  * in the structure, it will be allocated large enough to hold all
235  * of the necessary pointers
236  */
237
238 typedef struct _FcCacheSkip FcCacheSkip;
239
240 struct _FcCacheSkip {
241     FcCache         *cache;
242     FcRef           ref;
243     intptr_t        size;
244     dev_t           cache_dev;
245     ino_t           cache_ino;
246     time_t          cache_mtime;
247     FcCacheSkip     *next[1];
248 };
249
250 /*
251  * The head of the skip list; pointers for every possible level
252  * in the skip list, plus the largest level in the list
253  */
254
255 #define FC_CACHE_MAX_LEVEL  16
256
257 /* Protected by cache_lock below */
258 static FcCacheSkip      *fcCacheChains[FC_CACHE_MAX_LEVEL];
259 static int              fcCacheMaxLevel;
260
261
262 static FcMutex *cache_lock;
263
264 static void
265 lock_cache (void)
266 {
267   FcMutex *lock;
268 retry:
269   lock = fc_atomic_ptr_get (&cache_lock);
270   if (!lock) {
271     lock = (FcMutex *) malloc (sizeof (FcMutex));
272     FcMutexInit (lock);
273     if (!fc_atomic_ptr_cmpexch (&cache_lock, NULL, lock)) {
274       FcMutexFinish (lock);
275       goto retry;
276     }
277
278     FcMutexLock (lock);
279     /* Initialize random state */
280     FcRandom ();
281     return;
282   }
283   FcMutexLock (lock);
284 }
285
286 static void
287 unlock_cache (void)
288 {
289   FcMutexUnlock (cache_lock);
290 }
291
292 static void
293 free_lock (void)
294 {
295   FcMutex *lock;
296   lock = fc_atomic_ptr_get (&cache_lock);
297   if (lock && fc_atomic_ptr_cmpexch (&cache_lock, lock, NULL)) {
298     FcMutexFinish (lock);
299     free (lock);
300   }
301 }
302
303
304
305 /*
306  * Generate a random level number, distributed
307  * so that each level is 1/4 as likely as the one before
308  *
309  * Note that level numbers run 1 <= level <= MAX_LEVEL
310  */
311 static int
312 random_level (void)
313 {
314     /* tricky bit -- each bit is '1' 75% of the time */
315     long int    bits = FcRandom () | FcRandom ();
316     int level = 0;
317
318     while (++level < FC_CACHE_MAX_LEVEL)
319     {
320         if (bits & 1)
321             break;
322         bits >>= 1;
323     }
324     return level;
325 }
326
327 /*
328  * Insert cache into the list
329  */
330 static FcBool
331 FcCacheInsert (FcCache *cache, struct stat *cache_stat)
332 {
333     FcCacheSkip    **update[FC_CACHE_MAX_LEVEL];
334     FcCacheSkip    *s, **next;
335     int             i, level;
336
337     lock_cache ();
338
339     /*
340      * Find links along each chain
341      */
342     next = fcCacheChains;
343     for (i = fcCacheMaxLevel; --i >= 0; )
344     {
345         for (; (s = next[i]); next = s->next)
346             if (s->cache > cache)
347                 break;
348         update[i] = &next[i];
349     }
350
351     /*
352      * Create new list element
353      */
354     level = random_level ();
355     if (level > fcCacheMaxLevel)
356     {
357         level = fcCacheMaxLevel + 1;
358         update[fcCacheMaxLevel] = &fcCacheChains[fcCacheMaxLevel];
359         fcCacheMaxLevel = level;
360     }
361
362     s = malloc (sizeof (FcCacheSkip) + (level - 1) * sizeof (FcCacheSkip *));
363     if (!s)
364         return FcFalse;
365
366     s->cache = cache;
367     s->size = cache->size;
368     FcRefInit (&s->ref, 1);
369     if (cache_stat)
370     {
371         s->cache_dev = cache_stat->st_dev;
372         s->cache_ino = cache_stat->st_ino;
373         s->cache_mtime = cache_stat->st_mtime;
374     }
375     else
376     {
377         s->cache_dev = 0;
378         s->cache_ino = 0;
379         s->cache_mtime = 0;
380     }
381
382     /*
383      * Insert into all fcCacheChains
384      */
385     for (i = 0; i < level; i++)
386     {
387         s->next[i] = *update[i];
388         *update[i] = s;
389     }
390
391     unlock_cache ();
392     return FcTrue;
393 }
394
395 static FcCacheSkip *
396 FcCacheFindByAddrUnlocked (void *object)
397 {
398     int     i;
399     FcCacheSkip    **next = fcCacheChains;
400     FcCacheSkip    *s;
401
402     if (!object)
403         return NULL;
404
405     /*
406      * Walk chain pointers one level at a time
407      */
408     for (i = fcCacheMaxLevel; --i >= 0;)
409         while (next[i] && (char *) object >= ((char *) next[i]->cache + next[i]->size))
410             next = next[i]->next;
411     /*
412      * Here we are
413      */
414     s = next[0];
415     if (s && (char *) object < ((char *) s->cache + s->size))
416         return s;
417     return NULL;
418 }
419
420 static FcCacheSkip *
421 FcCacheFindByAddr (void *object)
422 {
423     FcCacheSkip *ret;
424     lock_cache ();
425     ret = FcCacheFindByAddrUnlocked (object);
426     unlock_cache ();
427     return ret;
428 }
429
430 static void
431 FcCacheRemoveUnlocked (FcCache *cache)
432 {
433     FcCacheSkip     **update[FC_CACHE_MAX_LEVEL];
434     FcCacheSkip     *s, **next;
435     int             i;
436
437     /*
438      * Find links along each chain
439      */
440     next = fcCacheChains;
441     for (i = fcCacheMaxLevel; --i >= 0; )
442     {
443         for (; (s = next[i]); next = s->next)
444             if (s->cache >= cache)
445                 break;
446         update[i] = &next[i];
447     }
448     s = next[0];
449     for (i = 0; i < fcCacheMaxLevel && *update[i] == s; i++)
450         *update[i] = s->next[i];
451     while (fcCacheMaxLevel > 0 && fcCacheChains[fcCacheMaxLevel - 1] == NULL)
452         fcCacheMaxLevel--;
453     free (s);
454 }
455
456 static FcCache *
457 FcCacheFindByStat (struct stat *cache_stat)
458 {
459     FcCacheSkip     *s;
460
461     lock_cache ();
462     for (s = fcCacheChains[0]; s; s = s->next[0])
463         if (s->cache_dev == cache_stat->st_dev &&
464             s->cache_ino == cache_stat->st_ino &&
465             s->cache_mtime == cache_stat->st_mtime)
466         {
467             FcRefInc (&s->ref);
468             unlock_cache ();
469             return s->cache;
470         }
471     unlock_cache ();
472     return NULL;
473 }
474
475 static void
476 FcDirCacheDisposeUnlocked (FcCache *cache)
477 {
478     FcCacheRemoveUnlocked (cache);
479
480     switch (cache->magic) {
481     case FC_CACHE_MAGIC_ALLOC:
482         free (cache);
483         break;
484     case FC_CACHE_MAGIC_MMAP:
485 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
486         munmap (cache, cache->size);
487 #elif defined(_WIN32)
488         UnmapViewOfFile (cache);
489 #endif
490         break;
491     }
492 }
493
494 void
495 FcCacheObjectReference (void *object)
496 {
497     FcCacheSkip *skip = FcCacheFindByAddr (object);
498
499     if (skip)
500         FcRefInc (&skip->ref);
501 }
502
503 void
504 FcCacheObjectDereference (void *object)
505 {
506     FcCacheSkip *skip;
507
508     lock_cache ();
509     skip = FcCacheFindByAddrUnlocked (object);
510     if (skip)
511     {
512         if (FcRefDec (&skip->ref) == 1)
513             FcDirCacheDisposeUnlocked (skip->cache);
514     }
515     unlock_cache ();
516 }
517
518 void
519 FcCacheFini (void)
520 {
521     int             i;
522
523     for (i = 0; i < FC_CACHE_MAX_LEVEL; i++)
524         assert (fcCacheChains[i] == NULL);
525     assert (fcCacheMaxLevel == 0);
526
527     free_lock ();
528 }
529
530 static FcBool
531 FcCacheTimeValid (FcCache *cache, struct stat *dir_stat)
532 {
533     struct stat dir_static;
534
535     if (!dir_stat)
536     {
537         if (FcStatChecksum (FcCacheDir (cache), &dir_static) < 0)
538             return FcFalse;
539         dir_stat = &dir_static;
540     }
541     if (FcDebug () & FC_DBG_CACHE)
542         printf ("FcCacheTimeValid dir \"%s\" cache checksum %d dir checksum %d\n",
543                 FcCacheDir (cache), cache->checksum, (int) dir_stat->st_mtime);
544     return cache->checksum == (int) dir_stat->st_mtime;
545 }
546
547 /*
548  * Map a cache file into memory
549  */
550 static FcCache *
551 FcDirCacheMapFd (int fd, struct stat *fd_stat, struct stat *dir_stat)
552 {
553     FcCache     *cache;
554     FcBool      allocated = FcFalse;
555
556     if (fd_stat->st_size < (int) sizeof (FcCache))
557         return NULL;
558     cache = FcCacheFindByStat (fd_stat);
559     if (cache)
560     {
561         if (FcCacheTimeValid (cache, dir_stat))
562             return cache;
563         FcDirCacheUnload (cache);
564         cache = NULL;
565     }
566
567     /*
568      * Large cache files are mmap'ed, smaller cache files are read. This
569      * balances the system cost of mmap against per-process memory usage.
570      */
571     if (FcCacheIsMmapSafe (fd) && fd_stat->st_size >= FC_CACHE_MIN_MMAP)
572     {
573 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
574         cache = mmap (0, fd_stat->st_size, PROT_READ, MAP_SHARED, fd, 0);
575 #ifdef HAVE_POSIX_FADVISE
576         posix_fadvise (fd, 0, fd_stat->st_size, POSIX_FADV_WILLNEED);
577 #endif
578         if (cache == MAP_FAILED)
579             cache = NULL;
580 #elif defined(_WIN32)
581         {
582             HANDLE hFileMap;
583
584             cache = NULL;
585             hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
586                                          PAGE_READONLY, 0, 0, NULL);
587             if (hFileMap != NULL)
588             {
589                 cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0,
590                                        fd_stat->st_size);
591                 CloseHandle (hFileMap);
592             }
593         }
594 #endif
595     }
596     if (!cache)
597     {
598         cache = malloc (fd_stat->st_size);
599         if (!cache)
600             return NULL;
601
602         if (read (fd, cache, fd_stat->st_size) != fd_stat->st_size)
603         {
604             free (cache);
605             return NULL;
606         }
607         allocated = FcTrue;
608     }
609     if (cache->magic != FC_CACHE_MAGIC_MMAP ||
610         cache->version < FC_CACHE_CONTENT_VERSION ||
611         cache->size != (intptr_t) fd_stat->st_size ||
612         !FcCacheTimeValid (cache, dir_stat) ||
613         !FcCacheInsert (cache, fd_stat))
614     {
615         if (allocated)
616             free (cache);
617         else
618         {
619 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
620             munmap (cache, fd_stat->st_size);
621 #elif defined(_WIN32)
622             UnmapViewOfFile (cache);
623 #endif
624         }
625         return NULL;
626     }
627
628     /* Mark allocated caches so they're freed rather than unmapped */
629     if (allocated)
630         cache->magic = FC_CACHE_MAGIC_ALLOC;
631         
632     return cache;
633 }
634
635 void
636 FcDirCacheReference (FcCache *cache, int nref)
637 {
638     FcCacheSkip *skip = FcCacheFindByAddr (cache);
639
640     if (skip)
641         FcRefAdd (&skip->ref, nref);
642 }
643
644 void
645 FcDirCacheUnload (FcCache *cache)
646 {
647     FcCacheObjectDereference (cache);
648 }
649
650 static FcBool
651 FcDirCacheMapHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, void *closure)
652 {
653     FcCache *cache = FcDirCacheMapFd (fd, fd_stat, dir_stat);
654
655     if (!cache)
656         return FcFalse;
657     *((FcCache **) closure) = cache;
658     return FcTrue;
659 }
660
661 FcCache *
662 FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
663 {
664     FcCache *cache = NULL;
665
666     if (!FcDirCacheProcess (config, dir,
667                             FcDirCacheMapHelper,
668                             &cache, cache_file))
669         return NULL;
670     return cache;
671 }
672
673 FcCache *
674 FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
675 {
676     int fd;
677     FcCache *cache;
678     struct stat my_file_stat;
679
680     if (!file_stat)
681         file_stat = &my_file_stat;
682     fd = FcDirCacheOpenFile (cache_file, file_stat);
683     if (fd < 0)
684         return NULL;
685     cache = FcDirCacheMapFd (fd, file_stat, NULL);
686     close (fd);
687     return cache;
688 }
689
690 /*
691  * Validate a cache file by reading the header and checking
692  * the magic number and the size field
693  */
694 static FcBool
695 FcDirCacheValidateHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, void *closure FC_UNUSED)
696 {
697     FcBool  ret = FcTrue;
698     FcCache     c;
699
700     if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache))
701         ret = FcFalse;
702     else if (c.magic != FC_CACHE_MAGIC_MMAP)
703         ret = FcFalse;
704     else if (c.version < FC_CACHE_CONTENT_VERSION)
705         ret = FcFalse;
706     else if (fd_stat->st_size != c.size)
707         ret = FcFalse;
708     else if (c.checksum != (int) dir_stat->st_mtime)
709         ret = FcFalse;
710     return ret;
711 }
712
713 static FcBool
714 FcDirCacheValidConfig (const FcChar8 *dir, FcConfig *config)
715 {
716     return FcDirCacheProcess (config, dir,
717                               FcDirCacheValidateHelper,
718                               NULL, NULL);
719 }
720
721 FcBool
722 FcDirCacheValid (const FcChar8 *dir)
723 {
724     FcConfig    *config;
725
726     config = FcConfigGetCurrent ();
727     if (!config)
728         return FcFalse;
729
730     return FcDirCacheValidConfig (dir, config);
731 }
732
733 /*
734  * Build a cache structure from the given contents
735  */
736 FcCache *
737 FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcStrSet *dirs)
738 {
739     FcSerialize *serialize = FcSerializeCreate ();
740     FcCache *cache;
741     int i;
742     FcChar8     *dir_serialize;
743     intptr_t    *dirs_serialize;
744     FcFontSet   *set_serialize;
745
746     if (!serialize)
747         return NULL;
748     /*
749      * Space for cache structure
750      */
751     FcSerializeReserve (serialize, sizeof (FcCache));
752     /*
753      * Directory name
754      */
755     if (!FcStrSerializeAlloc (serialize, dir))
756         goto bail1;
757     /*
758      * Subdirs
759      */
760     FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *));
761     for (i = 0; i < dirs->num; i++)
762         if (!FcStrSerializeAlloc (serialize, dirs->strs[i]))
763             goto bail1;
764
765     /*
766      * Patterns
767      */
768     if (!FcFontSetSerializeAlloc (serialize, set))
769         goto bail1;
770
771     /* Serialize layout complete. Now allocate space and fill it */
772     cache = malloc (serialize->size);
773     if (!cache)
774         goto bail1;
775     /* shut up valgrind */
776     memset (cache, 0, serialize->size);
777
778     serialize->linear = cache;
779
780     cache->magic = FC_CACHE_MAGIC_ALLOC;
781     cache->version = FC_CACHE_CONTENT_VERSION;
782     cache->size = serialize->size;
783     cache->checksum = (int) dir_stat->st_mtime;
784
785     /*
786      * Serialize directory name
787      */
788     dir_serialize = FcStrSerialize (serialize, dir);
789     if (!dir_serialize)
790         goto bail2;
791     cache->dir = FcPtrToOffset (cache, dir_serialize);
792
793     /*
794      * Serialize sub dirs
795      */
796     dirs_serialize = FcSerializePtr (serialize, dirs);
797     if (!dirs_serialize)
798         goto bail2;
799     cache->dirs = FcPtrToOffset (cache, dirs_serialize);
800     cache->dirs_count = dirs->num;
801     for (i = 0; i < dirs->num; i++)
802     {
803         FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]);
804         if (!d_serialize)
805             goto bail2;
806         dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize);
807     }
808
809     /*
810      * Serialize font set
811      */
812     set_serialize = FcFontSetSerialize (serialize, set);
813     if (!set_serialize)
814         goto bail2;
815     cache->set = FcPtrToOffset (cache, set_serialize);
816
817     FcSerializeDestroy (serialize);
818
819     FcCacheInsert (cache, NULL);
820
821     return cache;
822
823 bail2:
824     free (cache);
825 bail1:
826     FcSerializeDestroy (serialize);
827     return NULL;
828 }
829
830 /* write serialized state to the cache file */
831 FcBool
832 FcDirCacheWrite (FcCache *cache, FcConfig *config)
833 {
834     FcChar8         *dir = FcCacheDir (cache);
835     FcChar8         cache_base[CACHEBASE_LEN];
836     FcChar8         *cache_hashed;
837     int             fd;
838     FcAtomic        *atomic;
839     FcStrList       *list;
840     FcChar8         *cache_dir = NULL;
841     FcChar8         *test_dir, *d = NULL;
842     FcCacheSkip     *skip;
843     struct stat     cache_stat;
844     unsigned int    magic;
845     int             written;
846     const FcChar8   *sysroot = FcConfigGetSysRoot (config);
847
848     /*
849      * Write it to the first directory in the list which is writable
850      */
851
852     list = FcStrListCreate (config->cacheDirs);
853     if (!list)
854         return FcFalse;
855     while ((test_dir = FcStrListNext (list)))
856     {
857         if (d)
858             FcStrFree (d);
859         if (sysroot)
860             d = FcStrBuildFilename (sysroot, test_dir, NULL);
861         else
862             d = FcStrCopyFilename (test_dir);
863
864         if (access ((char *) d, W_OK) == 0)
865         {
866             cache_dir = FcStrCopyFilename (d);
867             break;
868         }
869         else
870         {
871             /*
872              * If the directory doesn't exist, try to create it
873              */
874             if (access ((char *) d, F_OK) == -1) {
875                 if (FcMakeDirectory (d))
876                 {
877                     cache_dir = FcStrCopyFilename (d);
878                     /* Create CACHEDIR.TAG */
879                     FcDirCacheCreateTagFile (d);
880                     break;
881                 }
882             }
883             /*
884              * Otherwise, try making it writable
885              */
886             else if (chmod ((char *) d, 0755) == 0)
887             {
888                 cache_dir = FcStrCopyFilename (d);
889                 /* Try to create CACHEDIR.TAG too */
890                 FcDirCacheCreateTagFile (d);
891                 break;
892             }
893         }
894     }
895     if (d)
896         FcStrFree (d);
897     FcStrListDone (list);
898     if (!cache_dir)
899         return FcFalse;
900
901     FcDirCacheBasename (dir, cache_base);
902     cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
903     if (!cache_hashed)
904         return FcFalse;
905     FcStrFree (cache_dir);
906
907     if (FcDebug () & FC_DBG_CACHE)
908         printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n",
909                 dir, cache_hashed);
910
911     atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
912     if (!atomic)
913         goto bail1;
914
915     if (!FcAtomicLock (atomic))
916         goto bail3;
917
918     fd = FcOpen((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
919     if (fd == -1)
920         goto bail4;
921
922     /* Temporarily switch magic to MMAP while writing to file */
923     magic = cache->magic;
924     if (magic != FC_CACHE_MAGIC_MMAP)
925         cache->magic = FC_CACHE_MAGIC_MMAP;
926
927     /*
928      * Write cache contents to file
929      */
930     written = write (fd, cache, cache->size);
931
932     /* Switch magic back */
933     if (magic != FC_CACHE_MAGIC_MMAP)
934         cache->magic = magic;
935
936     if (written != cache->size)
937     {
938         perror ("write cache");
939         goto bail5;
940     }
941
942     close(fd);
943     if (!FcAtomicReplaceOrig(atomic))
944         goto bail4;
945
946     /* If the file is small, update the cache chain entry such that the
947      * new cache file is not read again.  If it's large, we don't do that
948      * such that we reload it, using mmap, which is shared across processes.
949      */
950     if (cache->size < FC_CACHE_MIN_MMAP && FcStat (cache_hashed, &cache_stat))
951     {
952         lock_cache ();
953         if ((skip = FcCacheFindByAddrUnlocked (cache)))
954         {
955             skip->cache_dev = cache_stat.st_dev;
956             skip->cache_ino = cache_stat.st_ino;
957             skip->cache_mtime = cache_stat.st_mtime;
958         }
959         unlock_cache ();
960     }
961
962     FcStrFree (cache_hashed);
963     FcAtomicUnlock (atomic);
964     FcAtomicDestroy (atomic);
965     return FcTrue;
966
967  bail5:
968     close (fd);
969  bail4:
970     FcAtomicUnlock (atomic);
971  bail3:
972     FcAtomicDestroy (atomic);
973  bail1:
974     FcStrFree (cache_hashed);
975     return FcFalse;
976 }
977
978 FcBool
979 FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose)
980 {
981     DIR         *d;
982     struct dirent *ent;
983     FcChar8     *dir;
984     FcBool      ret = FcTrue;
985     FcBool      remove;
986     FcCache     *cache;
987     struct stat target_stat;
988     const FcChar8 *sysroot;
989
990     /* FIXME: this API needs to support non-current FcConfig */
991     sysroot = FcConfigGetSysRoot (NULL);
992     if (sysroot)
993         dir = FcStrBuildFilename (sysroot, cache_dir, NULL);
994     else
995         dir = FcStrCopyFilename (cache_dir);
996     if (!dir)
997     {
998         fprintf (stderr, "Fontconfig error: %s: out of memory\n", cache_dir);
999         return FcFalse;
1000     }
1001     if (access ((char *) dir, W_OK) != 0)
1002     {
1003         if (verbose || FcDebug () & FC_DBG_CACHE)
1004             printf ("%s: not cleaning %s cache directory\n", dir,
1005                     access ((char *) dir, F_OK) == 0 ? "unwritable" : "non-existent");
1006         goto bail0;
1007     }
1008     if (verbose || FcDebug () & FC_DBG_CACHE)
1009         printf ("%s: cleaning cache directory\n", dir);
1010     d = opendir ((char *) dir);
1011     if (!d)
1012     {
1013         perror ((char *) dir);
1014         ret = FcFalse;
1015         goto bail0;
1016     }
1017     while ((ent = readdir (d)))
1018     {
1019         FcChar8 *file_name;
1020         const FcChar8   *target_dir;
1021
1022         if (ent->d_name[0] == '.')
1023             continue;
1024         /* skip cache files for different architectures and */
1025         /* files which are not cache files at all */
1026         if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) ||
1027             strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX))
1028             continue;
1029
1030         file_name = FcStrBuildFilename (dir, (FcChar8 *)ent->d_name, NULL);
1031         if (!file_name)
1032         {
1033             fprintf (stderr, "Fontconfig error: %s: allocation failure\n", dir);
1034             ret = FcFalse;
1035             break;
1036         }
1037         remove = FcFalse;
1038         cache = FcDirCacheLoadFile (file_name, NULL);
1039         if (!cache)
1040         {
1041             if (verbose || FcDebug () & FC_DBG_CACHE)
1042                 printf ("%s: invalid cache file: %s\n", dir, ent->d_name);
1043             remove = FcTrue;
1044         }
1045         else
1046         {
1047             target_dir = FcCacheDir (cache);
1048             if (stat ((char *) target_dir, &target_stat) < 0)
1049             {
1050                 if (verbose || FcDebug () & FC_DBG_CACHE)
1051                     printf ("%s: %s: missing directory: %s \n",
1052                             dir, ent->d_name, target_dir);
1053                 remove = FcTrue;
1054             }
1055             FcDirCacheUnload (cache);
1056         }
1057         if (remove)
1058         {
1059             if (unlink ((char *) file_name) < 0)
1060             {
1061                 perror ((char *) file_name);
1062                 ret = FcFalse;
1063             }
1064         }
1065         FcStrFree (file_name);
1066     }
1067
1068     closedir (d);
1069   bail0:
1070     FcStrFree (dir);
1071
1072     return ret;
1073 }
1074
1075 /*
1076  * Hokey little macro trick to permit the definitions of C functions
1077  * with the same name as CPP macros
1078  */
1079 #define args1(x)            (x)
1080 #define args2(x,y)          (x,y)
1081
1082 const FcChar8 *
1083 FcCacheDir args1(const FcCache *c)
1084 {
1085     return FcCacheDir (c);
1086 }
1087
1088 FcFontSet *
1089 FcCacheCopySet args1(const FcCache *c)
1090 {
1091     FcFontSet   *old = FcCacheSet (c);
1092     FcFontSet   *new = FcFontSetCreate ();
1093     int         i;
1094
1095     if (!new)
1096         return NULL;
1097     for (i = 0; i < old->nfont; i++)
1098     {
1099         FcPattern   *font = FcFontSetFont (old, i);
1100         
1101         FcPatternReference (font);
1102         if (!FcFontSetAdd (new, font))
1103         {
1104             FcFontSetDestroy (new);
1105             return NULL;
1106         }
1107     }
1108     return new;
1109 }
1110
1111 const FcChar8 *
1112 FcCacheSubdir args2(const FcCache *c, int i)
1113 {
1114     return FcCacheSubdir (c, i);
1115 }
1116
1117 int
1118 FcCacheNumSubdir args1(const FcCache *c)
1119 {
1120     return c->dirs_count;
1121 }
1122
1123 int
1124 FcCacheNumFont args1(const FcCache *c)
1125 {
1126     return FcCacheSet(c)->nfont;
1127 }
1128
1129 /*
1130  * This code implements the MD5 message-digest algorithm.
1131  * The algorithm is due to Ron Rivest.  This code was
1132  * written by Colin Plumb in 1993, no copyright is claimed.
1133  * This code is in the public domain; do with it what you wish.
1134  *
1135  * Equivalent code is available from RSA Data Security, Inc.
1136  * This code has been tested against that, and is equivalent,
1137  * except that you don't need to include two pages of legalese
1138  * with every copy.
1139  *
1140  * To compute the message digest of a chunk of bytes, declare an
1141  * MD5Context structure, pass it to MD5Init, call MD5Update as
1142  * needed on buffers full of bytes, and then call MD5Final, which
1143  * will fill a supplied 16-byte array with the digest.
1144  */
1145
1146 #ifndef HIGHFIRST
1147 #define byteReverse(buf, len)   /* Nothing */
1148 #else
1149 /*
1150  * Note: this code is harmless on little-endian machines.
1151  */
1152 void byteReverse(unsigned char *buf, unsigned longs)
1153 {
1154     FcChar32 t;
1155     do {
1156         t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
1157             ((unsigned) buf[1] << 8 | buf[0]);
1158         *(FcChar32 *) buf = t;
1159         buf += 4;
1160     } while (--longs);
1161 }
1162 #endif
1163
1164 /*
1165  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
1166  * initialization constants.
1167  */
1168 static void MD5Init(struct MD5Context *ctx)
1169 {
1170     ctx->buf[0] = 0x67452301;
1171     ctx->buf[1] = 0xefcdab89;
1172     ctx->buf[2] = 0x98badcfe;
1173     ctx->buf[3] = 0x10325476;
1174
1175     ctx->bits[0] = 0;
1176     ctx->bits[1] = 0;
1177 }
1178
1179 /*
1180  * Update context to reflect the concatenation of another buffer full
1181  * of bytes.
1182  */
1183 static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned len)
1184 {
1185     FcChar32 t;
1186
1187     /* Update bitcount */
1188
1189     t = ctx->bits[0];
1190     if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t)
1191         ctx->bits[1]++;         /* Carry from low to high */
1192     ctx->bits[1] += len >> 29;
1193
1194     t = (t >> 3) & 0x3f;        /* Bytes already in shsInfo->data */
1195
1196     /* Handle any leading odd-sized chunks */
1197
1198     if (t) {
1199         unsigned char *p = (unsigned char *) ctx->in + t;
1200
1201         t = 64 - t;
1202         if (len < t) {
1203             memcpy(p, buf, len);
1204             return;
1205         }
1206         memcpy(p, buf, t);
1207         byteReverse(ctx->in, 16);
1208         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1209         buf += t;
1210         len -= t;
1211     }
1212     /* Process data in 64-byte chunks */
1213
1214     while (len >= 64) {
1215         memcpy(ctx->in, buf, 64);
1216         byteReverse(ctx->in, 16);
1217         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1218         buf += 64;
1219         len -= 64;
1220     }
1221
1222     /* Handle any remaining bytes of data. */
1223
1224     memcpy(ctx->in, buf, len);
1225 }
1226
1227 /*
1228  * Final wrapup - pad to 64-byte boundary with the bit pattern
1229  * 1 0* (64-bit count of bits processed, MSB-first)
1230  */
1231 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
1232 {
1233     unsigned count;
1234     unsigned char *p;
1235
1236     /* Compute number of bytes mod 64 */
1237     count = (ctx->bits[0] >> 3) & 0x3F;
1238
1239     /* Set the first char of padding to 0x80.  This is safe since there is
1240        always at least one byte free */
1241     p = ctx->in + count;
1242     *p++ = 0x80;
1243
1244     /* Bytes of padding needed to make 64 bytes */
1245     count = 64 - 1 - count;
1246
1247     /* Pad out to 56 mod 64 */
1248     if (count < 8) {
1249         /* Two lots of padding:  Pad the first block to 64 bytes */
1250         memset(p, 0, count);
1251         byteReverse(ctx->in, 16);
1252         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1253
1254         /* Now fill the next block with 56 bytes */
1255         memset(ctx->in, 0, 56);
1256     } else {
1257         /* Pad block to 56 bytes */
1258         memset(p, 0, count - 8);
1259     }
1260     byteReverse(ctx->in, 14);
1261
1262     /* Append length in bits and transform */
1263     ((FcChar32 *) ctx->in)[14] = ctx->bits[0];
1264     ((FcChar32 *) ctx->in)[15] = ctx->bits[1];
1265
1266     MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1267     byteReverse((unsigned char *) ctx->buf, 4);
1268     memcpy(digest, ctx->buf, 16);
1269     memset(ctx, 0, sizeof(*ctx));        /* In case it's sensitive */
1270 }
1271
1272
1273 /* The four core functions - F1 is optimized somewhat */
1274
1275 /* #define F1(x, y, z) (x & y | ~x & z) */
1276 #define F1(x, y, z) (z ^ (x & (y ^ z)))
1277 #define F2(x, y, z) F1(z, x, y)
1278 #define F3(x, y, z) (x ^ y ^ z)
1279 #define F4(x, y, z) (y ^ (x | ~z))
1280
1281 /* This is the central step in the MD5 algorithm. */
1282 #define MD5STEP(f, w, x, y, z, data, s) \
1283         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
1284
1285 /*
1286  * The core of the MD5 algorithm, this alters an existing MD5 hash to
1287  * reflect the addition of 16 longwords of new data.  MD5Update blocks
1288  * the data and converts bytes into longwords for this routine.
1289  */
1290 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16])
1291 {
1292     register FcChar32 a, b, c, d;
1293
1294     a = buf[0];
1295     b = buf[1];
1296     c = buf[2];
1297     d = buf[3];
1298
1299     MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
1300     MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
1301     MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
1302     MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
1303     MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
1304     MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
1305     MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
1306     MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
1307     MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
1308     MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
1309     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
1310     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
1311     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
1312     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
1313     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
1314     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
1315
1316     MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
1317     MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
1318     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
1319     MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
1320     MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
1321     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
1322     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
1323     MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
1324     MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
1325     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
1326     MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
1327     MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
1328     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
1329     MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
1330     MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
1331     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1332
1333     MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1334     MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1335     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1336     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1337     MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1338     MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1339     MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1340     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1341     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1342     MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1343     MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1344     MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1345     MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1346     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1347     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1348     MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1349
1350     MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1351     MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1352     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1353     MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1354     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1355     MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1356     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1357     MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1358     MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1359     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1360     MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1361     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1362     MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1363     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1364     MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1365     MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1366
1367     buf[0] += a;
1368     buf[1] += b;
1369     buf[2] += c;
1370     buf[3] += d;
1371 }
1372
1373 FcBool
1374 FcDirCacheCreateTagFile (const FcChar8 *cache_dir)
1375 {
1376     FcChar8             *cache_tag;
1377     int                  fd;
1378     FILE                *fp;
1379     FcAtomic            *atomic;
1380     static const FcChar8 cache_tag_contents[] =
1381         "Signature: 8a477f597d28d172789f06886806bc55\n"
1382         "# This file is a cache directory tag created by fontconfig.\n"
1383         "# For information about cache directory tags, see:\n"
1384         "#       http://www.brynosaurus.com/cachedir/\n";
1385     static size_t        cache_tag_contents_size = sizeof (cache_tag_contents) - 1;
1386     FcBool               ret = FcFalse;
1387
1388     if (!cache_dir)
1389         return FcFalse;
1390
1391     if (access ((char *) cache_dir, W_OK) == 0)
1392     {
1393         /* Create CACHEDIR.TAG */
1394         cache_tag = FcStrBuildFilename (cache_dir, "CACHEDIR.TAG", NULL);
1395         if (!cache_tag)
1396             return FcFalse;
1397         atomic = FcAtomicCreate ((FcChar8 *)cache_tag);
1398         if (!atomic)
1399             goto bail1;
1400         if (!FcAtomicLock (atomic))
1401             goto bail2;
1402         fd = FcOpen((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT, 0644);
1403         if (fd == -1)
1404             goto bail3;
1405         fp = fdopen(fd, "wb");
1406         if (fp == NULL)
1407             goto bail3;
1408
1409         fwrite(cache_tag_contents, cache_tag_contents_size, sizeof (FcChar8), fp);
1410         fclose(fp);
1411
1412         if (!FcAtomicReplaceOrig(atomic))
1413             goto bail3;
1414
1415         ret = FcTrue;
1416       bail3:
1417         FcAtomicUnlock (atomic);
1418       bail2:
1419         FcAtomicDestroy (atomic);
1420       bail1:
1421         FcStrFree (cache_tag);
1422     }
1423
1424     if (FcDebug () & FC_DBG_CACHE)
1425     {
1426         if (ret)
1427             printf ("Created CACHEDIR.TAG at %s\n", cache_dir);
1428         else
1429             printf ("Unable to create CACHEDIR.TAG at %s\n", cache_dir);
1430     }
1431
1432     return ret;
1433 }
1434
1435 void
1436 FcCacheCreateTagFile (const FcConfig *config)
1437 {
1438     FcChar8   *cache_dir = NULL, *d = NULL;
1439     FcStrList *list;
1440     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
1441
1442     list = FcConfigGetCacheDirs (config);
1443     if (!list)
1444         return;
1445
1446     while ((cache_dir = FcStrListNext (list)))
1447     {
1448         if (d)
1449             FcStrFree (d);
1450         if (sysroot)
1451             d = FcStrBuildFilename (sysroot, cache_dir, NULL);
1452         else
1453             d = FcStrCopyFilename (cache_dir);
1454         if (FcDirCacheCreateTagFile (d))
1455             break;
1456     }
1457     if (d)
1458         FcStrFree (d);
1459     FcStrListDone (list);
1460 }
1461
1462 #define __fccache__
1463 #include "fcaliastail.h"
1464 #undef __fccache__