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