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