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