Make cache refcounting 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     FcRef           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     FcRefInit (&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             FcRefInc (&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         FcRefInc (&skip->ref);
485 }
486
487 void
488 FcCacheObjectDereference (void *object)
489 {
490     FcCacheSkip *skip = FcCacheFindByAddr (object);
491
492     if (skip)
493     {
494         if (FcRefDec (&skip->ref) <= 1)
495             FcDirCacheDispose (skip->cache);
496     }
497 }
498
499 void
500 FcCacheFini (void)
501 {
502     int             i;
503
504     for (i = 0; i < FC_CACHE_MAX_LEVEL; i++)
505         assert (fcCacheChains[i] == NULL);
506     assert (fcCacheMaxLevel == 0);
507 }
508
509 static FcBool
510 FcCacheTimeValid (FcCache *cache, struct stat *dir_stat)
511 {
512     struct stat dir_static;
513
514     if (!dir_stat)
515     {
516         if (FcStatChecksum (FcCacheDir (cache), &dir_static) < 0)
517             return FcFalse;
518         dir_stat = &dir_static;
519     }
520     if (FcDebug () & FC_DBG_CACHE)
521         printf ("FcCacheTimeValid dir \"%s\" cache checksum %d dir checksum %d\n",
522                 FcCacheDir (cache), cache->checksum, (int) dir_stat->st_mtime);
523     return cache->checksum == (int) dir_stat->st_mtime;
524 }
525
526 /*
527  * Map a cache file into memory
528  */
529 static FcCache *
530 FcDirCacheMapFd (int fd, struct stat *fd_stat, struct stat *dir_stat)
531 {
532     FcCache     *cache;
533     FcBool      allocated = FcFalse;
534
535     if (fd_stat->st_size < (int) sizeof (FcCache))
536         return NULL;
537     cache = FcCacheFindByStat (fd_stat);
538     if (cache)
539     {
540         if (FcCacheTimeValid (cache, dir_stat))
541             return cache;
542         FcDirCacheUnload (cache);
543         cache = NULL;
544     }
545
546     /*
547      * Large cache files are mmap'ed, smaller cache files are read. This
548      * balances the system cost of mmap against per-process memory usage.
549      */
550     if (FcCacheIsMmapSafe (fd) && fd_stat->st_size >= FC_CACHE_MIN_MMAP)
551     {
552 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
553         cache = mmap (0, fd_stat->st_size, PROT_READ, MAP_SHARED, fd, 0);
554 #ifdef HAVE_POSIX_FADVISE
555         posix_fadvise (fd, 0, fd_stat->st_size, POSIX_FADV_WILLNEED);
556 #endif
557         if (cache == MAP_FAILED)
558             cache = NULL;
559 #elif defined(_WIN32)
560         {
561             HANDLE hFileMap;
562
563             cache = NULL;
564             hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
565                                          PAGE_READONLY, 0, 0, NULL);
566             if (hFileMap != NULL)
567             {
568                 cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0,
569                                        fd_stat->st_size);
570                 CloseHandle (hFileMap);
571             }
572         }
573 #endif
574     }
575     if (!cache)
576     {
577         cache = malloc (fd_stat->st_size);
578         if (!cache)
579             return NULL;
580
581         if (read (fd, cache, fd_stat->st_size) != fd_stat->st_size)
582         {
583             free (cache);
584             return NULL;
585         }
586         allocated = FcTrue;
587     }
588     if (cache->magic != FC_CACHE_MAGIC_MMAP ||
589         cache->version < FC_CACHE_CONTENT_VERSION ||
590         cache->size != (intptr_t) fd_stat->st_size ||
591         !FcCacheTimeValid (cache, dir_stat) ||
592         !FcCacheInsert (cache, fd_stat))
593     {
594         if (allocated)
595             free (cache);
596         else
597         {
598 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
599             munmap (cache, fd_stat->st_size);
600 #elif defined(_WIN32)
601             UnmapViewOfFile (cache);
602 #endif
603         }
604         return NULL;
605     }
606
607     /* Mark allocated caches so they're freed rather than unmapped */
608     if (allocated)
609         cache->magic = FC_CACHE_MAGIC_ALLOC;
610         
611     return cache;
612 }
613
614 void
615 FcDirCacheReference (FcCache *cache, int nref)
616 {
617     FcCacheSkip *skip = FcCacheFindByAddr (cache);
618
619     if (skip)
620         FcRefAdd (&skip->ref, nref);
621 }
622
623 void
624 FcDirCacheUnload (FcCache *cache)
625 {
626     FcCacheObjectDereference (cache);
627 }
628
629 static FcBool
630 FcDirCacheMapHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, void *closure)
631 {
632     FcCache *cache = FcDirCacheMapFd (fd, fd_stat, dir_stat);
633
634     if (!cache)
635         return FcFalse;
636     *((FcCache **) closure) = cache;
637     return FcTrue;
638 }
639
640 FcCache *
641 FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
642 {
643     FcCache *cache = NULL;
644
645     if (!FcDirCacheProcess (config, dir,
646                             FcDirCacheMapHelper,
647                             &cache, cache_file))
648         return NULL;
649     return cache;
650 }
651
652 FcCache *
653 FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
654 {
655     int fd;
656     FcCache *cache;
657     struct stat my_file_stat;
658
659     if (!file_stat)
660         file_stat = &my_file_stat;
661     fd = FcDirCacheOpenFile (cache_file, file_stat);
662     if (fd < 0)
663         return NULL;
664     cache = FcDirCacheMapFd (fd, file_stat, NULL);
665     close (fd);
666     return cache;
667 }
668
669 /*
670  * Validate a cache file by reading the header and checking
671  * the magic number and the size field
672  */
673 static FcBool
674 FcDirCacheValidateHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, void *closure FC_UNUSED)
675 {
676     FcBool  ret = FcTrue;
677     FcCache     c;
678
679     if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache))
680         ret = FcFalse;
681     else if (c.magic != FC_CACHE_MAGIC_MMAP)
682         ret = FcFalse;
683     else if (c.version < FC_CACHE_CONTENT_VERSION)
684         ret = FcFalse;
685     else if (fd_stat->st_size != c.size)
686         ret = FcFalse;
687     else if (c.checksum != (int) dir_stat->st_mtime)
688         ret = FcFalse;
689     return ret;
690 }
691
692 static FcBool
693 FcDirCacheValidConfig (const FcChar8 *dir, FcConfig *config)
694 {
695     return FcDirCacheProcess (config, dir,
696                               FcDirCacheValidateHelper,
697                               NULL, NULL);
698 }
699
700 FcBool
701 FcDirCacheValid (const FcChar8 *dir)
702 {
703     FcConfig    *config;
704
705     config = FcConfigGetCurrent ();
706     if (!config)
707         return FcFalse;
708
709     return FcDirCacheValidConfig (dir, config);
710 }
711
712 /*
713  * Build a cache structure from the given contents
714  */
715 FcCache *
716 FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcStrSet *dirs)
717 {
718     FcSerialize *serialize = FcSerializeCreate ();
719     FcCache *cache;
720     int i;
721     FcChar8     *dir_serialize;
722     intptr_t    *dirs_serialize;
723     FcFontSet   *set_serialize;
724
725     if (!serialize)
726         return NULL;
727     /*
728      * Space for cache structure
729      */
730     FcSerializeReserve (serialize, sizeof (FcCache));
731     /*
732      * Directory name
733      */
734     if (!FcStrSerializeAlloc (serialize, dir))
735         goto bail1;
736     /*
737      * Subdirs
738      */
739     FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *));
740     for (i = 0; i < dirs->num; i++)
741         if (!FcStrSerializeAlloc (serialize, dirs->strs[i]))
742             goto bail1;
743
744     /*
745      * Patterns
746      */
747     if (!FcFontSetSerializeAlloc (serialize, set))
748         goto bail1;
749
750     /* Serialize layout complete. Now allocate space and fill it */
751     cache = malloc (serialize->size);
752     if (!cache)
753         goto bail1;
754     /* shut up valgrind */
755     memset (cache, 0, serialize->size);
756
757     serialize->linear = cache;
758
759     cache->magic = FC_CACHE_MAGIC_ALLOC;
760     cache->version = FC_CACHE_CONTENT_VERSION;
761     cache->size = serialize->size;
762     cache->checksum = (int) dir_stat->st_mtime;
763
764     /*
765      * Serialize directory name
766      */
767     dir_serialize = FcStrSerialize (serialize, dir);
768     if (!dir_serialize)
769         goto bail2;
770     cache->dir = FcPtrToOffset (cache, dir_serialize);
771
772     /*
773      * Serialize sub dirs
774      */
775     dirs_serialize = FcSerializePtr (serialize, dirs);
776     if (!dirs_serialize)
777         goto bail2;
778     cache->dirs = FcPtrToOffset (cache, dirs_serialize);
779     cache->dirs_count = dirs->num;
780     for (i = 0; i < dirs->num; i++)
781     {
782         FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]);
783         if (!d_serialize)
784             goto bail2;
785         dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize);
786     }
787
788     /*
789      * Serialize font set
790      */
791     set_serialize = FcFontSetSerialize (serialize, set);
792     if (!set_serialize)
793         goto bail2;
794     cache->set = FcPtrToOffset (cache, set_serialize);
795
796     FcSerializeDestroy (serialize);
797
798     FcCacheInsert (cache, NULL);
799
800     return cache;
801
802 bail2:
803     free (cache);
804 bail1:
805     FcSerializeDestroy (serialize);
806     return NULL;
807 }
808
809
810 #ifdef _WIN32
811 #define mkdir(path,mode) _mkdir(path)
812 #endif
813
814 static FcBool
815 FcMakeDirectory (const FcChar8 *dir)
816 {
817     FcChar8 *parent;
818     FcBool  ret;
819
820     if (strlen ((char *) dir) == 0)
821         return FcFalse;
822
823     parent = FcStrDirname (dir);
824     if (!parent)
825         return FcFalse;
826     if (access ((char *) parent, F_OK) == 0)
827         ret = mkdir ((char *) dir, 0755) == 0 && chmod ((char *) dir, 0755) == 0;
828     else if (access ((char *) parent, F_OK) == -1)
829         ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0755) == 0) && chmod ((char *) dir, 0755) == 0;
830     else
831         ret = FcFalse;
832     FcStrFree (parent);
833     return ret;
834 }
835
836 /* write serialized state to the cache file */
837 FcBool
838 FcDirCacheWrite (FcCache *cache, FcConfig *config)
839 {
840     FcChar8         *dir = FcCacheDir (cache);
841     FcChar8         cache_base[CACHEBASE_LEN];
842     FcChar8         *cache_hashed;
843     int             fd;
844     FcAtomic        *atomic;
845     FcStrList       *list;
846     FcChar8         *cache_dir = NULL;
847     FcChar8         *test_dir;
848     FcCacheSkip     *skip;
849     struct stat     cache_stat;
850     unsigned int    magic;
851     int             written;
852
853     /*
854      * Write it to the first directory in the list which is writable
855      */
856
857     list = FcStrListCreate (config->cacheDirs);
858     if (!list)
859         return FcFalse;
860     while ((test_dir = FcStrListNext (list))) {
861         if (access ((char *) test_dir, W_OK) == 0)
862         {
863             cache_dir = test_dir;
864             break;
865         }
866         else
867         {
868             /*
869              * If the directory doesn't exist, try to create it
870              */
871             if (access ((char *) test_dir, F_OK) == -1) {
872                 if (FcMakeDirectory (test_dir))
873                 {
874                     cache_dir = test_dir;
875                     /* Create CACHEDIR.TAG */
876                     FcDirCacheCreateTagFile (cache_dir);
877                     break;
878                 }
879             }
880             /*
881              * Otherwise, try making it writable
882              */
883             else if (chmod ((char *) test_dir, 0755) == 0)
884             {
885                 cache_dir = test_dir;
886                 /* Try to create CACHEDIR.TAG too */
887                 FcDirCacheCreateTagFile (cache_dir);
888                 break;
889             }
890         }
891     }
892     FcStrListDone (list);
893     if (!cache_dir)
894         return FcFalse;
895
896     FcDirCacheBasename (dir, cache_base);
897     cache_hashed = FcStrPlus (cache_dir, cache_base);
898     if (!cache_hashed)
899         return FcFalse;
900
901     if (FcDebug () & FC_DBG_CACHE)
902         printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n",
903                 dir, cache_hashed);
904
905     atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
906     if (!atomic)
907         goto bail1;
908
909     if (!FcAtomicLock (atomic))
910         goto bail3;
911
912     fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
913     if (fd == -1)
914         goto bail4;
915
916     /* Temporarily switch magic to MMAP while writing to file */
917     magic = cache->magic;
918     if (magic != FC_CACHE_MAGIC_MMAP)
919         cache->magic = FC_CACHE_MAGIC_MMAP;
920
921     /*
922      * Write cache contents to file
923      */
924     written = write (fd, cache, cache->size);
925
926     /* Switch magic back */
927     if (magic != FC_CACHE_MAGIC_MMAP)
928         cache->magic = magic;
929
930     if (written != cache->size)
931     {
932         perror ("write cache");
933         goto bail5;
934     }
935
936     close(fd);
937     if (!FcAtomicReplaceOrig(atomic))
938         goto bail4;
939
940     /* If the file is small, update the cache chain entry such that the
941      * new cache file is not read again.  If it's large, we don't do that
942      * such that we reload it, using mmap, which is shared across processes.
943      */
944     if (cache->size < FC_CACHE_MIN_MMAP &&
945         (skip = FcCacheFindByAddr (cache)) &&
946         FcStat (cache_hashed, &cache_stat))
947     {
948         skip->cache_dev = cache_stat.st_dev;
949         skip->cache_ino = cache_stat.st_ino;
950         skip->cache_mtime = cache_stat.st_mtime;
951     }
952
953     FcStrFree (cache_hashed);
954     FcAtomicUnlock (atomic);
955     FcAtomicDestroy (atomic);
956     return FcTrue;
957
958  bail5:
959     close (fd);
960  bail4:
961     FcAtomicUnlock (atomic);
962  bail3:
963     FcAtomicDestroy (atomic);
964  bail1:
965     FcStrFree (cache_hashed);
966     return FcFalse;
967 }
968
969 FcBool
970 FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose)
971 {
972     DIR         *d;
973     struct dirent *ent;
974     FcChar8     *dir_base;
975     FcBool      ret = FcTrue;
976     FcBool      remove;
977     FcCache     *cache;
978     struct stat target_stat;
979
980     dir_base = FcStrPlus (cache_dir, (FcChar8 *) FC_DIR_SEPARATOR_S);
981     if (!dir_base)
982     {
983         fprintf (stderr, "Fontconfig error: %s: out of memory\n", cache_dir);
984         return FcFalse;
985     }
986     if (access ((char *) cache_dir, W_OK) != 0)
987     {
988         if (verbose || FcDebug () & FC_DBG_CACHE)
989             printf ("%s: not cleaning %s cache directory\n", cache_dir,
990                     access ((char *) cache_dir, F_OK) == 0 ? "unwritable" : "non-existent");
991         goto bail0;
992     }
993     if (verbose || FcDebug () & FC_DBG_CACHE)
994         printf ("%s: cleaning cache directory\n", cache_dir);
995     d = opendir ((char *) cache_dir);
996     if (!d)
997     {
998         perror ((char *) cache_dir);
999         ret = FcFalse;
1000         goto bail0;
1001     }
1002     while ((ent = readdir (d)))
1003     {
1004         FcChar8 *file_name;
1005         const FcChar8   *target_dir;
1006
1007         if (ent->d_name[0] == '.')
1008             continue;
1009         /* skip cache files for different architectures and */
1010         /* files which are not cache files at all */
1011         if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) ||
1012             strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX))
1013             continue;
1014
1015         file_name = FcStrPlus (dir_base, (FcChar8 *) ent->d_name);
1016         if (!file_name)
1017         {
1018             fprintf (stderr, "Fontconfig error: %s: allocation failure\n", cache_dir);
1019             ret = FcFalse;
1020             break;
1021         }
1022         remove = FcFalse;
1023         cache = FcDirCacheLoadFile (file_name, NULL);
1024         if (!cache)
1025         {
1026             if (verbose || FcDebug () & FC_DBG_CACHE)
1027                 printf ("%s: invalid cache file: %s\n", cache_dir, ent->d_name);
1028             remove = FcTrue;
1029         }
1030         else
1031         {
1032             target_dir = FcCacheDir (cache);
1033             if (stat ((char *) target_dir, &target_stat) < 0)
1034             {
1035                 if (verbose || FcDebug () & FC_DBG_CACHE)
1036                     printf ("%s: %s: missing directory: %s \n",
1037                             cache_dir, ent->d_name, target_dir);
1038                 remove = FcTrue;
1039             }
1040         }
1041         if (remove)
1042         {
1043             if (unlink ((char *) file_name) < 0)
1044             {
1045                 perror ((char *) file_name);
1046                 ret = FcFalse;
1047             }
1048         }
1049         FcDirCacheUnload (cache);
1050         FcStrFree (file_name);
1051     }
1052
1053     closedir (d);
1054   bail0:
1055     FcStrFree (dir_base);
1056
1057     return ret;
1058 }
1059
1060 /*
1061  * Hokey little macro trick to permit the definitions of C functions
1062  * with the same name as CPP macros
1063  */
1064 #define args1(x)            (x)
1065 #define args2(x,y)          (x,y)
1066
1067 const FcChar8 *
1068 FcCacheDir args1(const FcCache *c)
1069 {
1070     return FcCacheDir (c);
1071 }
1072
1073 FcFontSet *
1074 FcCacheCopySet args1(const FcCache *c)
1075 {
1076     FcFontSet   *old = FcCacheSet (c);
1077     FcFontSet   *new = FcFontSetCreate ();
1078     int         i;
1079
1080     if (!new)
1081         return NULL;
1082     for (i = 0; i < old->nfont; i++)
1083     {
1084         FcPattern   *font = FcFontSetFont (old, i);
1085         
1086         FcPatternReference (font);
1087         if (!FcFontSetAdd (new, font))
1088         {
1089             FcFontSetDestroy (new);
1090             return NULL;
1091         }
1092     }
1093     return new;
1094 }
1095
1096 const FcChar8 *
1097 FcCacheSubdir args2(const FcCache *c, int i)
1098 {
1099     return FcCacheSubdir (c, i);
1100 }
1101
1102 int
1103 FcCacheNumSubdir args1(const FcCache *c)
1104 {
1105     return c->dirs_count;
1106 }
1107
1108 int
1109 FcCacheNumFont args1(const FcCache *c)
1110 {
1111     return FcCacheSet(c)->nfont;
1112 }
1113
1114 /*
1115  * This code implements the MD5 message-digest algorithm.
1116  * The algorithm is due to Ron Rivest.  This code was
1117  * written by Colin Plumb in 1993, no copyright is claimed.
1118  * This code is in the public domain; do with it what you wish.
1119  *
1120  * Equivalent code is available from RSA Data Security, Inc.
1121  * This code has been tested against that, and is equivalent,
1122  * except that you don't need to include two pages of legalese
1123  * with every copy.
1124  *
1125  * To compute the message digest of a chunk of bytes, declare an
1126  * MD5Context structure, pass it to MD5Init, call MD5Update as
1127  * needed on buffers full of bytes, and then call MD5Final, which
1128  * will fill a supplied 16-byte array with the digest.
1129  */
1130
1131 #ifndef HIGHFIRST
1132 #define byteReverse(buf, len)   /* Nothing */
1133 #else
1134 /*
1135  * Note: this code is harmless on little-endian machines.
1136  */
1137 void byteReverse(unsigned char *buf, unsigned longs)
1138 {
1139     FcChar32 t;
1140     do {
1141         t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
1142             ((unsigned) buf[1] << 8 | buf[0]);
1143         *(FcChar32 *) buf = t;
1144         buf += 4;
1145     } while (--longs);
1146 }
1147 #endif
1148
1149 /*
1150  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
1151  * initialization constants.
1152  */
1153 static void MD5Init(struct MD5Context *ctx)
1154 {
1155     ctx->buf[0] = 0x67452301;
1156     ctx->buf[1] = 0xefcdab89;
1157     ctx->buf[2] = 0x98badcfe;
1158     ctx->buf[3] = 0x10325476;
1159
1160     ctx->bits[0] = 0;
1161     ctx->bits[1] = 0;
1162 }
1163
1164 /*
1165  * Update context to reflect the concatenation of another buffer full
1166  * of bytes.
1167  */
1168 static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned len)
1169 {
1170     FcChar32 t;
1171
1172     /* Update bitcount */
1173
1174     t = ctx->bits[0];
1175     if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t)
1176         ctx->bits[1]++;         /* Carry from low to high */
1177     ctx->bits[1] += len >> 29;
1178
1179     t = (t >> 3) & 0x3f;        /* Bytes already in shsInfo->data */
1180
1181     /* Handle any leading odd-sized chunks */
1182
1183     if (t) {
1184         unsigned char *p = (unsigned char *) ctx->in + t;
1185
1186         t = 64 - t;
1187         if (len < t) {
1188             memcpy(p, buf, len);
1189             return;
1190         }
1191         memcpy(p, buf, t);
1192         byteReverse(ctx->in, 16);
1193         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1194         buf += t;
1195         len -= t;
1196     }
1197     /* Process data in 64-byte chunks */
1198
1199     while (len >= 64) {
1200         memcpy(ctx->in, buf, 64);
1201         byteReverse(ctx->in, 16);
1202         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1203         buf += 64;
1204         len -= 64;
1205     }
1206
1207     /* Handle any remaining bytes of data. */
1208
1209     memcpy(ctx->in, buf, len);
1210 }
1211
1212 /*
1213  * Final wrapup - pad to 64-byte boundary with the bit pattern
1214  * 1 0* (64-bit count of bits processed, MSB-first)
1215  */
1216 static void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
1217 {
1218     unsigned count;
1219     unsigned char *p;
1220
1221     /* Compute number of bytes mod 64 */
1222     count = (ctx->bits[0] >> 3) & 0x3F;
1223
1224     /* Set the first char of padding to 0x80.  This is safe since there is
1225        always at least one byte free */
1226     p = ctx->in + count;
1227     *p++ = 0x80;
1228
1229     /* Bytes of padding needed to make 64 bytes */
1230     count = 64 - 1 - count;
1231
1232     /* Pad out to 56 mod 64 */
1233     if (count < 8) {
1234         /* Two lots of padding:  Pad the first block to 64 bytes */
1235         memset(p, 0, count);
1236         byteReverse(ctx->in, 16);
1237         MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1238
1239         /* Now fill the next block with 56 bytes */
1240         memset(ctx->in, 0, 56);
1241     } else {
1242         /* Pad block to 56 bytes */
1243         memset(p, 0, count - 8);
1244     }
1245     byteReverse(ctx->in, 14);
1246
1247     /* Append length in bits and transform */
1248     ((FcChar32 *) ctx->in)[14] = ctx->bits[0];
1249     ((FcChar32 *) ctx->in)[15] = ctx->bits[1];
1250
1251     MD5Transform(ctx->buf, (FcChar32 *) ctx->in);
1252     byteReverse((unsigned char *) ctx->buf, 4);
1253     memcpy(digest, ctx->buf, 16);
1254     memset(ctx, 0, sizeof(*ctx));        /* In case it's sensitive */
1255 }
1256
1257
1258 /* The four core functions - F1 is optimized somewhat */
1259
1260 /* #define F1(x, y, z) (x & y | ~x & z) */
1261 #define F1(x, y, z) (z ^ (x & (y ^ z)))
1262 #define F2(x, y, z) F1(z, x, y)
1263 #define F3(x, y, z) (x ^ y ^ z)
1264 #define F4(x, y, z) (y ^ (x | ~z))
1265
1266 /* This is the central step in the MD5 algorithm. */
1267 #define MD5STEP(f, w, x, y, z, data, s) \
1268         ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
1269
1270 /*
1271  * The core of the MD5 algorithm, this alters an existing MD5 hash to
1272  * reflect the addition of 16 longwords of new data.  MD5Update blocks
1273  * the data and converts bytes into longwords for this routine.
1274  */
1275 static void MD5Transform(FcChar32 buf[4], FcChar32 in[16])
1276 {
1277     register FcChar32 a, b, c, d;
1278
1279     a = buf[0];
1280     b = buf[1];
1281     c = buf[2];
1282     d = buf[3];
1283
1284     MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
1285     MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
1286     MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
1287     MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
1288     MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
1289     MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
1290     MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
1291     MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
1292     MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
1293     MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
1294     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
1295     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
1296     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
1297     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
1298     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
1299     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
1300
1301     MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
1302     MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
1303     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
1304     MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
1305     MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
1306     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
1307     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
1308     MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
1309     MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
1310     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
1311     MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
1312     MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
1313     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
1314     MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
1315     MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
1316     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
1317
1318     MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
1319     MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
1320     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
1321     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
1322     MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
1323     MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
1324     MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
1325     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
1326     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
1327     MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
1328     MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
1329     MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
1330     MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
1331     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
1332     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
1333     MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
1334
1335     MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
1336     MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
1337     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
1338     MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
1339     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
1340     MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
1341     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
1342     MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
1343     MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
1344     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
1345     MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
1346     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
1347     MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
1348     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
1349     MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
1350     MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
1351
1352     buf[0] += a;
1353     buf[1] += b;
1354     buf[2] += c;
1355     buf[3] += d;
1356 }
1357
1358 FcBool
1359 FcDirCacheCreateTagFile (const FcChar8 *cache_dir)
1360 {
1361     FcChar8             *cache_tag;
1362     int                  fd;
1363     FILE                *fp;
1364     FcAtomic            *atomic;
1365     static const FcChar8 cache_tag_contents[] =
1366         "Signature: 8a477f597d28d172789f06886806bc55\n"
1367         "# This file is a cache directory tag created by fontconfig.\n"
1368         "# For information about cache directory tags, see:\n"
1369         "#       http://www.brynosaurus.com/cachedir/\n";
1370     static size_t        cache_tag_contents_size = sizeof (cache_tag_contents) - 1;
1371     FcBool               ret = FcFalse;
1372
1373     if (!cache_dir)
1374         return FcFalse;
1375
1376     if (access ((char *) cache_dir, W_OK) == 0)
1377     {
1378         /* Create CACHEDIR.TAG */
1379         cache_tag = FcStrPlus (cache_dir, (const FcChar8 *) FC_DIR_SEPARATOR_S "CACHEDIR.TAG");
1380         if (!cache_tag)
1381             return FcFalse;
1382         atomic = FcAtomicCreate ((FcChar8 *)cache_tag);
1383         if (!atomic)
1384             goto bail1;
1385         if (!FcAtomicLock (atomic))
1386             goto bail2;
1387         fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT, 0644);
1388         if (fd == -1)
1389             goto bail3;
1390         fp = fdopen(fd, "wb");
1391         if (fp == NULL)
1392             goto bail3;
1393
1394         fwrite(cache_tag_contents, cache_tag_contents_size, sizeof (FcChar8), fp);
1395         fclose(fp);
1396
1397         if (!FcAtomicReplaceOrig(atomic))
1398             goto bail3;
1399
1400         ret = FcTrue;
1401       bail3:
1402         FcAtomicUnlock (atomic);
1403       bail2:
1404         FcAtomicDestroy (atomic);
1405       bail1:
1406         FcStrFree (cache_tag);
1407     }
1408
1409     if (FcDebug () & FC_DBG_CACHE)
1410     {
1411         if (ret)
1412             printf ("Created CACHEDIR.TAG at %s\n", cache_dir);
1413         else
1414             printf ("Unable to create CACHEDIR.TAG at %s\n", cache_dir);
1415     }
1416
1417     return ret;
1418 }
1419
1420 void
1421 FcCacheCreateTagFile (const FcConfig *config)
1422 {
1423     FcChar8   *cache_dir = NULL;
1424     FcStrList *list;
1425
1426     list = FcConfigGetCacheDirs (config);
1427     if (!list)
1428         return;
1429
1430     while ((cache_dir = FcStrListNext (list)))
1431     {
1432         if (FcDirCacheCreateTagFile (cache_dir))
1433             break;
1434     }
1435     FcStrListDone (list);
1436 }
1437
1438 #define __fccache__
1439 #include "fcaliastail.h"
1440 #undef __fccache__