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