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