Imported Upstream version 2.14.2
[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 "fcmd5.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <fcntl.h>
29 #ifdef HAVE_DIRENT_H
30 #include <dirent.h>
31 #endif
32 #include <string.h>
33 #include <limits.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #ifndef _WIN32
38   #include <sys/time.h>
39 #else
40   #include <winsock2.h> /* for struct timeval */
41 #endif
42
43 #include <assert.h>
44 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
45 #  include <unistd.h>
46 #  include <sys/mman.h>
47 #endif
48 #if defined(_WIN32)
49 #include <sys/locking.h>
50 #endif
51
52 #ifndef O_BINARY
53 #define O_BINARY 0
54 #endif
55
56 FcBool
57 FcDirCacheCreateUUID (FcChar8  *dir,
58                       FcBool    force,
59                       FcConfig *config)
60 {
61     return FcTrue;
62 }
63
64 FcBool
65 FcDirCacheDeleteUUID (const FcChar8  *dir,
66                       FcConfig       *config)
67 {
68     FcBool ret = FcTrue;
69 #ifndef _WIN32
70     const FcChar8 *sysroot;
71     FcChar8 *target, *d;
72     struct stat statb;
73     struct timeval times[2];
74
75     config = FcConfigReference (config);
76     if (!config)
77         return FcFalse;
78     sysroot = FcConfigGetSysRoot (config);
79     if (sysroot)
80         d = FcStrBuildFilename (sysroot, dir, NULL);
81     else
82         d = FcStrBuildFilename (dir, NULL);
83     if (FcStat (d, &statb) != 0)
84     {
85         ret = FcFalse;
86         goto bail;
87     }
88     target = FcStrBuildFilename (d, ".uuid", NULL);
89     ret = unlink ((char *) target) == 0;
90     if (ret)
91     {
92         times[0].tv_sec = statb.st_atime;
93         times[1].tv_sec = statb.st_mtime;
94 #ifdef HAVE_STRUCT_STAT_ST_MTIM
95         times[0].tv_usec = statb.st_atim.tv_nsec / 1000;
96         times[1].tv_usec = statb.st_mtim.tv_nsec / 1000;
97 #else
98         times[0].tv_usec = 0;
99         times[1].tv_usec = 0;
100 #endif
101         if (utimes ((const char *) d, times) != 0)
102         {
103             fprintf (stderr, "Unable to revert mtime: %s\n", d);
104         }
105     }
106     FcStrFree (target);
107 bail:
108     FcStrFree (d);
109 #endif
110     FcConfigDestroy (config);
111
112     return ret;
113 }
114
115 #define CACHEBASE_LEN (1 + 36 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX))
116
117 static FcBool
118 FcCacheIsMmapSafe (int fd)
119 {
120     enum {
121       MMAP_NOT_INITIALIZED = 0,
122       MMAP_USE,
123       MMAP_DONT_USE,
124       MMAP_CHECK_FS,
125     } status;
126     static void *static_status;
127
128     status = (intptr_t) fc_atomic_ptr_get (&static_status);
129
130     if (status == MMAP_NOT_INITIALIZED)
131     {
132         const char *env = getenv ("FONTCONFIG_USE_MMAP");
133         FcBool use;
134         if (env && FcNameBool ((const FcChar8 *) env, &use))
135             status =  use ? MMAP_USE : MMAP_DONT_USE;
136         else
137             status = MMAP_CHECK_FS;
138         (void) fc_atomic_ptr_cmpexch (&static_status, NULL, (void *) (intptr_t) status);
139     }
140
141     if (status == MMAP_CHECK_FS)
142         return FcIsFsMmapSafe (fd);
143     else
144         return status == MMAP_USE;
145
146 }
147
148 static const char bin2hex[] = { '0', '1', '2', '3',
149                                 '4', '5', '6', '7',
150                                 '8', '9', 'a', 'b',
151                                 'c', 'd', 'e', 'f' };
152
153 static FcChar8 *
154 FcDirCacheBasenameMD5 (FcConfig *config, const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
155 {
156     FcChar8             *mapped_dir = NULL;
157     unsigned char       hash[16];
158     FcChar8             *hex_hash, *key = NULL;
159     int                 cnt;
160     struct MD5Context   ctx;
161     const FcChar8       *salt, *orig_dir = NULL;
162
163     salt = FcConfigMapSalt (config, dir);
164     /* Obtain a path where "dir" is mapped to.
165      * In case:
166      * <remap-dir as-path="/usr/share/fonts">/run/host/fonts</remap-dir>
167      *
168      * FcConfigMapFontPath (config, "/run/host/fonts") will returns "/usr/share/fonts".
169      */
170     mapped_dir = FcConfigMapFontPath(config, dir);
171     if (mapped_dir)
172     {
173         orig_dir = dir;
174         dir = mapped_dir;
175     }
176     if (salt)
177     {
178         size_t dl = strlen ((const char *) dir);
179         size_t sl = strlen ((const char *) salt);
180
181         key = (FcChar8 *) malloc (dl + sl + 1);
182         memcpy (key, dir, dl);
183         memcpy (key + dl, salt, sl + 1);
184         key[dl + sl] = 0;
185         if (!orig_dir)
186                 orig_dir = dir;
187         dir = key;
188     }
189     MD5Init (&ctx);
190     MD5Update (&ctx, (const unsigned char *)dir, strlen ((const char *) dir));
191
192     MD5Final (hash, &ctx);
193
194     if (key)
195         FcStrFree (key);
196
197     cache_base[0] = '/';
198     hex_hash = cache_base + 1;
199     for (cnt = 0; cnt < 16; ++cnt)
200     {
201         hex_hash[2*cnt  ] = bin2hex[hash[cnt] >> 4];
202         hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
203     }
204     hex_hash[2*cnt] = 0;
205     strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
206     if (FcDebug() & FC_DBG_CACHE)
207     {
208         printf ("cache: %s (dir: %s%s%s%s%s%s)\n", cache_base, orig_dir ? orig_dir : dir, mapped_dir ? " (mapped to " : "", mapped_dir ? (char *)mapped_dir : "", mapped_dir ? ")" : "", salt ? ", salt: " : "", salt ? (char *)salt : "");
209     }
210
211     if (mapped_dir)
212         FcStrFree(mapped_dir);
213
214     return cache_base;
215 }
216
217 #ifndef _WIN32
218 static FcChar8 *
219 FcDirCacheBasenameUUID (FcConfig *config, const FcChar8 *dir, FcChar8 cache_base[CACHEBASE_LEN])
220 {
221     FcChar8 *target, *fuuid;
222     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
223     int fd;
224
225     /* We don't need to apply remapping here. because .uuid was created at that very directory
226      * to determine the cache name no matter where it was mapped to.
227      */
228     cache_base[0] = 0;
229     if (sysroot)
230         target = FcStrBuildFilename (sysroot, dir, NULL);
231     else
232         target = FcStrdup (dir);
233     fuuid = FcStrBuildFilename (target, ".uuid", NULL);
234     if ((fd = FcOpen ((char *) fuuid, O_RDONLY)) != -1)
235     {
236         char suuid[37];
237         ssize_t len;
238
239         memset (suuid, 0, sizeof (suuid));
240         len = read (fd, suuid, 36);
241         suuid[36] = 0;
242         close (fd);
243         if (len < 0)
244             goto bail;
245         cache_base[0] = '/';
246         strcpy ((char *)&cache_base[1], suuid);
247         strcat ((char *) cache_base, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX);
248         if (FcDebug () & FC_DBG_CACHE)
249         {
250             printf ("cache fallbacks to: %s (dir: %s)\n", cache_base, dir);
251         }
252     }
253 bail:
254     FcStrFree (fuuid);
255     FcStrFree (target);
256
257     return cache_base;
258 }
259 #endif
260
261 FcBool
262 FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
263 {
264     FcChar8     *cache_hashed = NULL;
265     FcChar8     cache_base[CACHEBASE_LEN];
266 #ifndef _WIN32
267     FcChar8     uuid_cache_base[CACHEBASE_LEN];
268 #endif
269     FcStrList   *list;
270     FcChar8     *cache_dir;
271     const FcChar8 *sysroot;
272     FcBool      ret = FcTrue;
273
274     config = FcConfigReference (config);
275     if (!config)
276         return FcFalse;
277     sysroot = FcConfigGetSysRoot (config);
278
279     FcDirCacheBasenameMD5 (config, dir, cache_base);
280 #ifndef _WIN32
281     FcDirCacheBasenameUUID (config, dir, uuid_cache_base);
282 #endif
283
284     list = FcStrListCreate (config->cacheDirs);
285     if (!list)
286     {
287         ret = FcFalse;
288         goto bail;
289     }
290
291     while ((cache_dir = FcStrListNext (list)))
292     {
293         if (sysroot)
294             cache_hashed = FcStrBuildFilename (sysroot, cache_dir, cache_base, NULL);
295         else
296             cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
297         if (!cache_hashed)
298             break;
299         (void) unlink ((char *) cache_hashed);
300         FcStrFree (cache_hashed);
301 #ifndef _WIN32
302         if (uuid_cache_base[0] != 0)
303         {
304             if (sysroot)
305                 cache_hashed = FcStrBuildFilename (sysroot, cache_dir, uuid_cache_base, NULL);
306             else
307                 cache_hashed = FcStrBuildFilename (cache_dir, uuid_cache_base, NULL);
308             if (!cache_hashed)
309                 break;
310             (void) unlink ((char *) cache_hashed);
311             FcStrFree (cache_hashed);
312         }
313 #endif
314     }
315     FcStrListDone (list);
316     FcDirCacheDeleteUUID (dir, config);
317     /* return FcFalse if something went wrong */
318     if (cache_dir)
319         ret = FcFalse;
320 bail:
321     FcConfigDestroy (config);
322
323     return ret;
324 }
325
326 static int
327 FcDirCacheOpenFile (const FcChar8 *cache_file, struct stat *file_stat)
328 {
329     int fd;
330
331 #ifdef _WIN32
332     if (FcStat (cache_file, file_stat) < 0)
333         return -1;
334 #endif
335     fd = FcOpen((char *) cache_file, O_RDONLY | O_BINARY);
336     if (fd < 0)
337         return fd;
338 #ifndef _WIN32
339     if (fstat (fd, file_stat) < 0)
340     {
341         close (fd);
342         return -1;
343     }
344 #endif
345     return fd;
346 }
347
348 /*
349  * Look for a cache file for the specified dir. Attempt
350  * to use each one we find, stopping when the callback
351  * indicates success
352  */
353 static FcBool
354 FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
355                    FcBool (*callback) (FcConfig *config, int fd, struct stat *fd_stat,
356                                        struct stat *dir_stat, struct timeval *cache_mtime, void *closure),
357                    void *closure, FcChar8 **cache_file_ret)
358 {
359     int         fd = -1;
360     FcChar8     cache_base[CACHEBASE_LEN];
361     FcStrList   *list;
362     FcChar8     *cache_dir, *d;
363     struct stat file_stat, dir_stat;
364     FcBool      ret = FcFalse;
365     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
366     struct timeval latest_mtime = (struct timeval){ 0 };
367
368     if (sysroot)
369         d = FcStrBuildFilename (sysroot, dir, NULL);
370     else
371         d = FcStrdup (dir);
372     if (FcStatChecksum (d, &dir_stat) < 0)
373     {
374         FcStrFree (d);
375         return FcFalse;
376     }
377     FcStrFree (d);
378
379     FcDirCacheBasenameMD5 (config, dir, cache_base);
380
381     list = FcStrListCreate (config->cacheDirs);
382     if (!list)
383         return FcFalse;
384
385     while ((cache_dir = FcStrListNext (list)))
386     {
387         FcChar8 *cache_hashed;
388 #ifndef _WIN32
389         FcBool retried = FcFalse;
390 #endif
391
392         if (sysroot)
393             cache_hashed = FcStrBuildFilename (sysroot, cache_dir, cache_base, NULL);
394         else
395             cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
396         if (!cache_hashed)
397             break;
398 #ifndef _WIN32
399       retry:
400 #endif
401         fd = FcDirCacheOpenFile (cache_hashed, &file_stat);
402         if (fd >= 0) {
403             ret = (*callback) (config, fd, &file_stat, &dir_stat, &latest_mtime, closure);
404             close (fd);
405             if (ret)
406             {
407                 if (cache_file_ret)
408                 {
409                     if (*cache_file_ret)
410                         FcStrFree (*cache_file_ret);
411                     *cache_file_ret = cache_hashed;
412                 }
413                 else
414                     FcStrFree (cache_hashed);
415             }
416             else
417                 FcStrFree (cache_hashed);
418         }
419 #ifndef _WIN32
420         else if (!retried)
421         {
422             FcChar8     uuid_cache_base[CACHEBASE_LEN];
423
424             retried = FcTrue;
425             FcDirCacheBasenameUUID (config, dir, uuid_cache_base);
426             if (uuid_cache_base[0] != 0)
427             {
428                 FcStrFree (cache_hashed);
429                 if (sysroot)
430                     cache_hashed = FcStrBuildFilename (sysroot, cache_dir, uuid_cache_base, NULL);
431                 else
432                     cache_hashed = FcStrBuildFilename (cache_dir, uuid_cache_base, NULL);
433                 if (!cache_hashed)
434                     break;
435                 goto retry;
436             }
437             else
438                 FcStrFree (cache_hashed);
439         }
440 #endif
441         else
442             FcStrFree (cache_hashed);
443     }
444     FcStrListDone (list);
445
446     if (closure)
447         return !!(*((FcCache **)closure) != NULL);
448     return ret;
449 }
450
451 #define FC_CACHE_MIN_MMAP   1024
452
453 /*
454  * Skip list element, make sure the 'next' pointer is the last thing
455  * in the structure, it will be allocated large enough to hold all
456  * of the necessary pointers
457  */
458
459 typedef struct _FcCacheSkip FcCacheSkip;
460
461 struct _FcCacheSkip {
462     FcCache         *cache;
463     FcRef           ref;
464     intptr_t        size;
465     void           *allocated;
466     dev_t           cache_dev;
467     ino_t           cache_ino;
468     time_t          cache_mtime;
469     long            cache_mtime_nano;
470     FcCacheSkip     *next[1];
471 };
472
473 /*
474  * The head of the skip list; pointers for every possible level
475  * in the skip list, plus the largest level in the list
476  */
477
478 #define FC_CACHE_MAX_LEVEL  16
479
480 /* Protected by cache_lock below */
481 static FcCacheSkip      *fcCacheChains[FC_CACHE_MAX_LEVEL];
482 static int              fcCacheMaxLevel;
483
484
485 static FcMutex *cache_lock;
486
487 static void
488 lock_cache (void)
489 {
490   FcMutex *lock;
491 retry:
492   lock = fc_atomic_ptr_get (&cache_lock);
493   if (!lock) {
494     lock = (FcMutex *) malloc (sizeof (FcMutex));
495     FcMutexInit (lock);
496     if (!fc_atomic_ptr_cmpexch (&cache_lock, NULL, lock)) {
497       FcMutexFinish (lock);
498       free (lock);
499       goto retry;
500     }
501
502     FcMutexLock (lock);
503     /* Initialize random state */
504     FcRandom ();
505     return;
506   }
507   FcMutexLock (lock);
508 }
509
510 static void
511 unlock_cache (void)
512 {
513   FcMutex *lock;
514   lock = fc_atomic_ptr_get (&cache_lock);
515   FcMutexUnlock (lock);
516 }
517
518 static void
519 free_lock (void)
520 {
521   FcMutex *lock;
522   lock = fc_atomic_ptr_get (&cache_lock);
523   if (lock && fc_atomic_ptr_cmpexch (&cache_lock, lock, NULL)) {
524     FcMutexFinish (lock);
525     free (lock);
526   }
527 }
528
529
530
531 /*
532  * Generate a random level number, distributed
533  * so that each level is 1/4 as likely as the one before
534  *
535  * Note that level numbers run 1 <= level <= MAX_LEVEL
536  */
537 static int
538 random_level (void)
539 {
540     /* tricky bit -- each bit is '1' 75% of the time */
541     long int    bits = FcRandom () | FcRandom ();
542     int level = 0;
543
544     while (++level < FC_CACHE_MAX_LEVEL)
545     {
546         if (bits & 1)
547             break;
548         bits >>= 1;
549     }
550     return level;
551 }
552
553 /*
554  * Insert cache into the list
555  */
556 static FcBool
557 FcCacheInsert (FcCache *cache, struct stat *cache_stat)
558 {
559     FcCacheSkip    **update[FC_CACHE_MAX_LEVEL];
560     FcCacheSkip    *s, **next;
561     int             i, level;
562
563     lock_cache ();
564
565     /*
566      * Find links along each chain
567      */
568     next = fcCacheChains;
569     for (i = fcCacheMaxLevel; --i >= 0; )
570     {
571         for (; (s = next[i]); next = s->next)
572             if (s->cache > cache)
573                 break;
574         update[i] = &next[i];
575     }
576
577     /*
578      * Create new list element
579      */
580     level = random_level ();
581     if (level > fcCacheMaxLevel)
582     {
583         level = fcCacheMaxLevel + 1;
584         update[fcCacheMaxLevel] = &fcCacheChains[fcCacheMaxLevel];
585         fcCacheMaxLevel = level;
586     }
587
588     s = malloc (sizeof (FcCacheSkip) + (level - 1) * sizeof (FcCacheSkip *));
589     if (!s)
590         return FcFalse;
591
592     s->cache = cache;
593     s->size = cache->size;
594     s->allocated = NULL;
595     FcRefInit (&s->ref, 1);
596     if (cache_stat)
597     {
598         s->cache_dev = cache_stat->st_dev;
599         s->cache_ino = cache_stat->st_ino;
600         s->cache_mtime = cache_stat->st_mtime;
601 #ifdef HAVE_STRUCT_STAT_ST_MTIM
602         s->cache_mtime_nano = cache_stat->st_mtim.tv_nsec;
603 #else
604         s->cache_mtime_nano = 0;
605 #endif
606     }
607     else
608     {
609         s->cache_dev = 0;
610         s->cache_ino = 0;
611         s->cache_mtime = 0;
612         s->cache_mtime_nano = 0;
613     }
614
615     /*
616      * Insert into all fcCacheChains
617      */
618     for (i = 0; i < level; i++)
619     {
620         s->next[i] = *update[i];
621         *update[i] = s;
622     }
623
624     unlock_cache ();
625     return FcTrue;
626 }
627
628 static FcCacheSkip *
629 FcCacheFindByAddrUnlocked (void *object)
630 {
631     int     i;
632     FcCacheSkip    **next = fcCacheChains;
633     FcCacheSkip    *s;
634
635     if (!object)
636         return NULL;
637
638     /*
639      * Walk chain pointers one level at a time
640      */
641     for (i = fcCacheMaxLevel; --i >= 0;)
642         while (next[i] && (char *) object >= ((char *) next[i]->cache + next[i]->size))
643             next = next[i]->next;
644     /*
645      * Here we are
646      */
647     s = next[0];
648     if (s && (char *) object < ((char *) s->cache + s->size))
649         return s;
650     return NULL;
651 }
652
653 static FcCacheSkip *
654 FcCacheFindByAddr (void *object)
655 {
656     FcCacheSkip *ret;
657     lock_cache ();
658     ret = FcCacheFindByAddrUnlocked (object);
659     unlock_cache ();
660     return ret;
661 }
662
663 static void
664 FcCacheRemoveUnlocked (FcCache *cache)
665 {
666     FcCacheSkip     **update[FC_CACHE_MAX_LEVEL];
667     FcCacheSkip     *s, **next;
668     int             i;
669     void            *allocated;
670
671     /*
672      * Find links along each chain
673      */
674     next = fcCacheChains;
675     for (i = fcCacheMaxLevel; --i >= 0; )
676     {
677         for (; (s = next[i]); next = s->next)
678             if (s->cache >= cache)
679                 break;
680         update[i] = &next[i];
681     }
682     s = next[0];
683     for (i = 0; i < fcCacheMaxLevel && *update[i] == s; i++)
684         *update[i] = s->next[i];
685     while (fcCacheMaxLevel > 0 && fcCacheChains[fcCacheMaxLevel - 1] == NULL)
686         fcCacheMaxLevel--;
687
688     if (s)
689     {
690         allocated = s->allocated;
691         while (allocated)
692         {
693             /* First element in allocated chunk is the free list */
694             next = *(void **)allocated;
695             free (allocated);
696             allocated = next;
697         }
698         free (s);
699     }
700 }
701
702 static FcCache *
703 FcCacheFindByStat (struct stat *cache_stat)
704 {
705     FcCacheSkip     *s;
706
707     lock_cache ();
708     for (s = fcCacheChains[0]; s; s = s->next[0])
709         if (s->cache_dev == cache_stat->st_dev &&
710             s->cache_ino == cache_stat->st_ino &&
711             s->cache_mtime == cache_stat->st_mtime)
712         {
713 #ifdef HAVE_STRUCT_STAT_ST_MTIM
714             if (s->cache_mtime_nano != cache_stat->st_mtim.tv_nsec)
715                 continue;
716 #endif
717             FcRefInc (&s->ref);
718             unlock_cache ();
719             return s->cache;
720         }
721     unlock_cache ();
722     return NULL;
723 }
724
725 static void
726 FcDirCacheDisposeUnlocked (FcCache *cache)
727 {
728     FcCacheRemoveUnlocked (cache);
729
730     switch (cache->magic) {
731     case FC_CACHE_MAGIC_ALLOC:
732         free (cache);
733         break;
734     case FC_CACHE_MAGIC_MMAP:
735 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
736         munmap (cache, cache->size);
737 #elif defined(_WIN32)
738         UnmapViewOfFile (cache);
739 #endif
740         break;
741     }
742 }
743
744 void
745 FcCacheObjectReference (void *object)
746 {
747     FcCacheSkip *skip = FcCacheFindByAddr (object);
748
749     if (skip)
750         FcRefInc (&skip->ref);
751 }
752
753 void
754 FcCacheObjectDereference (void *object)
755 {
756     FcCacheSkip *skip;
757
758     lock_cache ();
759     skip = FcCacheFindByAddrUnlocked (object);
760     if (skip)
761     {
762         if (FcRefDec (&skip->ref) == 1)
763             FcDirCacheDisposeUnlocked (skip->cache);
764     }
765     unlock_cache ();
766 }
767
768 void *
769 FcCacheAllocate (FcCache *cache, size_t len)
770 {
771     FcCacheSkip *skip;
772     void *allocated = NULL;
773
774     lock_cache ();
775     skip = FcCacheFindByAddrUnlocked (cache);
776     if (skip)
777     {
778       void *chunk = malloc (sizeof (void *) + len);
779       if (chunk)
780       {
781           /* First element in allocated chunk is the free list */
782           *(void **)chunk = skip->allocated;
783           skip->allocated = chunk;
784           /* Return the rest */
785           allocated = ((FcChar8 *)chunk) + sizeof (void *);
786       }
787     }
788     unlock_cache ();
789     return allocated;
790 }
791
792 void
793 FcCacheFini (void)
794 {
795     int             i;
796
797     if (FcDebug() & FC_DBG_CACHE)
798     {
799         for (i = 0; i < FC_CACHE_MAX_LEVEL; i++)
800         {
801             if (fcCacheChains[i] != NULL)
802             {
803                 FcCacheSkip *s = fcCacheChains[i];
804                 fprintf(stderr, "Fontconfig error: not freed %p (dir: %s, refcount %" FC_ATOMIC_INT_FORMAT ")\n", s->cache, FcCacheDir(s->cache), s->ref.count);
805             }
806         }
807     }
808
809     free_lock ();
810 }
811
812 static FcBool
813 FcCacheTimeValid (FcConfig *config, FcCache *cache, struct stat *dir_stat)
814 {
815     struct stat dir_static;
816     FcBool fnano = FcTrue;
817
818     if (!dir_stat)
819     {
820         const FcChar8 *sysroot = FcConfigGetSysRoot (config);
821         FcChar8 *d;
822
823         if (sysroot)
824             d = FcStrBuildFilename (sysroot, FcCacheDir (cache), NULL);
825         else
826             d = FcStrdup (FcCacheDir (cache));
827         if (FcStatChecksum (d, &dir_static) < 0)
828         {
829             FcStrFree (d);
830             return FcFalse;
831         }
832         FcStrFree (d);
833         dir_stat = &dir_static;
834     }
835 #ifdef HAVE_STRUCT_STAT_ST_MTIM
836     fnano = (cache->checksum_nano == dir_stat->st_mtim.tv_nsec);
837     if (FcDebug () & FC_DBG_CACHE)
838         printf ("FcCacheTimeValid dir \"%s\" cache checksum %d.%ld dir checksum %d.%ld\n",
839                 FcCacheDir (cache), cache->checksum, (long)cache->checksum_nano, (int) dir_stat->st_mtime, dir_stat->st_mtim.tv_nsec);
840 #else
841     if (FcDebug () & FC_DBG_CACHE)
842         printf ("FcCacheTimeValid dir \"%s\" cache checksum %d dir checksum %d\n",
843                 FcCacheDir (cache), cache->checksum, (int) dir_stat->st_mtime);
844 #endif
845
846     return dir_stat->st_mtime == 0 || (cache->checksum == (int) dir_stat->st_mtime && fnano);
847 }
848
849 static FcBool
850 FcCacheOffsetsValid (FcCache *cache)
851 {
852     char                *base = (char *)cache;
853     char                *end = base + cache->size;
854     intptr_t            *dirs;
855     FcFontSet           *fs;
856     int                  i, j;
857
858     if (cache->dir < 0 || cache->dir > cache->size - sizeof (intptr_t) ||
859         memchr (base + cache->dir, '\0', cache->size - cache->dir) == NULL)
860         return FcFalse;
861
862     if (cache->dirs < 0 || cache->dirs >= cache->size ||
863         cache->dirs_count < 0 ||
864         cache->dirs_count > (cache->size - cache->dirs) / sizeof (intptr_t))
865         return FcFalse;
866
867     dirs = FcCacheDirs (cache);
868     if (dirs)
869     {
870         for (i = 0; i < cache->dirs_count; i++)
871         {
872             FcChar8     *dir;
873
874             if (dirs[i] < 0 ||
875                 dirs[i] > end - (char *) dirs - sizeof (intptr_t))
876                 return FcFalse;
877
878             dir = FcOffsetToPtr (dirs, dirs[i], FcChar8);
879             if (memchr (dir, '\0', end - (char *) dir) == NULL)
880                 return FcFalse;
881          }
882     }
883
884     if (cache->set < 0 || cache->set > cache->size - sizeof (FcFontSet))
885         return FcFalse;
886
887     fs = FcCacheSet (cache);
888     if (fs)
889     {
890         if (fs->nfont > (end - (char *) fs) / sizeof (FcPattern))
891             return FcFalse;
892
893         if (!FcIsEncodedOffset(fs->fonts))
894             return FcFalse;
895
896         for (i = 0; i < fs->nfont; i++)
897         {
898             FcPattern           *font = FcFontSetFont (fs, i);
899             FcPatternElt        *e;
900             FcValueListPtr       l;
901             char                *last_offset;
902
903             if ((char *) font < base ||
904                 (char *) font > end - sizeof (FcFontSet) ||
905                 font->elts_offset < 0 ||
906                 font->elts_offset > end - (char *) font ||
907                 font->num > (end - (char *) font - font->elts_offset) / sizeof (FcPatternElt) ||
908                 !FcRefIsConst (&font->ref))
909                 return FcFalse;
910
911
912             e = FcPatternElts(font);
913             if (e->values != 0 && !FcIsEncodedOffset(e->values))
914                 return FcFalse;
915
916             for (j = 0; j < font->num; j++)
917             {
918                 last_offset = (char *) font + font->elts_offset;
919                 for (l = FcPatternEltValues(&e[j]); l; l = FcValueListNext(l))
920                 {
921                     if ((char *) l < last_offset || (char *) l > end - sizeof (*l) ||
922                         (l->next != NULL && !FcIsEncodedOffset(l->next)))
923                         return FcFalse;
924                     last_offset = (char *) l + 1;
925                 }
926             }
927         }
928     }
929
930     return FcTrue;
931 }
932
933 /*
934  * Map a cache file into memory
935  */
936 static FcCache *
937 FcDirCacheMapFd (FcConfig *config, int fd, struct stat *fd_stat, struct stat *dir_stat)
938 {
939     FcCache     *cache;
940     FcBool      allocated = FcFalse;
941
942     if (fd_stat->st_size > INTPTR_MAX ||
943         fd_stat->st_size < (int) sizeof (FcCache))
944         return NULL;
945     cache = FcCacheFindByStat (fd_stat);
946     if (cache)
947     {
948         if (FcCacheTimeValid (config, cache, dir_stat))
949             return cache;
950         FcDirCacheUnload (cache);
951         cache = NULL;
952     }
953
954     /*
955      * Large cache files are mmap'ed, smaller cache files are read. This
956      * balances the system cost of mmap against per-process memory usage.
957      */
958     if (FcCacheIsMmapSafe (fd) && fd_stat->st_size >= FC_CACHE_MIN_MMAP)
959     {
960 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
961         cache = mmap (0, fd_stat->st_size, PROT_READ, MAP_SHARED, fd, 0);
962 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
963         posix_fadvise (fd, 0, fd_stat->st_size, POSIX_FADV_WILLNEED);
964 #endif
965         if (cache == MAP_FAILED)
966             cache = NULL;
967 #elif defined(_WIN32)
968         {
969             HANDLE hFileMap;
970
971             cache = NULL;
972             hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
973                                          PAGE_READONLY, 0, 0, NULL);
974             if (hFileMap != NULL)
975             {
976                 cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0,
977                                        fd_stat->st_size);
978                 CloseHandle (hFileMap);
979             }
980         }
981 #endif
982     }
983     if (!cache)
984     {
985         cache = malloc (fd_stat->st_size);
986         if (!cache)
987             return NULL;
988
989         if (read (fd, cache, fd_stat->st_size) != fd_stat->st_size)
990         {
991             free (cache);
992             return NULL;
993         }
994         allocated = FcTrue;
995     }
996     if (cache->magic != FC_CACHE_MAGIC_MMAP ||
997         cache->version < FC_CACHE_VERSION_NUMBER ||
998         cache->size != (intptr_t) fd_stat->st_size ||
999         !FcCacheOffsetsValid (cache) ||
1000         !FcCacheTimeValid (config, cache, dir_stat) ||
1001         !FcCacheInsert (cache, fd_stat))
1002     {
1003         if (allocated)
1004             free (cache);
1005         else
1006         {
1007 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
1008             munmap (cache, fd_stat->st_size);
1009 #elif defined(_WIN32)
1010             UnmapViewOfFile (cache);
1011 #endif
1012         }
1013         return NULL;
1014     }
1015
1016     /* Mark allocated caches so they're freed rather than unmapped */
1017     if (allocated)
1018         cache->magic = FC_CACHE_MAGIC_ALLOC;
1019
1020     return cache;
1021 }
1022
1023 void
1024 FcDirCacheReference (FcCache *cache, int nref)
1025 {
1026     FcCacheSkip *skip = FcCacheFindByAddr (cache);
1027
1028     if (skip)
1029         FcRefAdd (&skip->ref, nref);
1030 }
1031
1032 void
1033 FcDirCacheUnload (FcCache *cache)
1034 {
1035     FcCacheObjectDereference (cache);
1036 }
1037
1038 static FcBool
1039 FcDirCacheMapHelper (FcConfig *config, int fd, struct stat *fd_stat, struct stat *dir_stat, struct timeval *latest_cache_mtime, void *closure)
1040 {
1041     FcCache *cache = FcDirCacheMapFd (config, fd, fd_stat, dir_stat);
1042     struct timeval cache_mtime, zero_mtime = { 0, 0}, dir_mtime;
1043
1044     if (!cache)
1045         return FcFalse;
1046     cache_mtime.tv_sec = fd_stat->st_mtime;
1047     dir_mtime.tv_sec = dir_stat->st_mtime;
1048 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1049     cache_mtime.tv_usec = fd_stat->st_mtim.tv_nsec / 1000;
1050     dir_mtime.tv_usec = dir_stat->st_mtim.tv_nsec / 1000;
1051 #else
1052     cache_mtime.tv_usec = 0;
1053     dir_mtime.tv_usec = 0;
1054 #endif
1055     /* special take care of OSTree */
1056     if (!timercmp (&zero_mtime, &dir_mtime, !=))
1057     {
1058         if (!timercmp (&zero_mtime, &cache_mtime, !=))
1059         {
1060             if (*((FcCache **) closure))
1061                 FcDirCacheUnload (*((FcCache **) closure));
1062         }
1063         else if (*((FcCache **) closure) && !timercmp (&zero_mtime, latest_cache_mtime, !=))
1064         {
1065             FcDirCacheUnload (cache);
1066             return FcFalse;
1067         }
1068         else if (timercmp (latest_cache_mtime, &cache_mtime, <))
1069         {
1070             if (*((FcCache **) closure))
1071                 FcDirCacheUnload (*((FcCache **) closure));
1072         }
1073     }
1074     else if (timercmp (latest_cache_mtime, &cache_mtime, <))
1075     {
1076         if (*((FcCache **) closure))
1077             FcDirCacheUnload (*((FcCache **) closure));
1078     }
1079     else
1080     {
1081         FcDirCacheUnload (cache);
1082         return FcFalse;
1083     }
1084     latest_cache_mtime->tv_sec = cache_mtime.tv_sec;
1085     latest_cache_mtime->tv_usec = cache_mtime.tv_usec;
1086     *((FcCache **) closure) = cache;
1087     return FcTrue;
1088 }
1089
1090 FcCache *
1091 FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file)
1092 {
1093     FcCache *cache = NULL;
1094
1095     config = FcConfigReference (config);
1096     if (!config)
1097         return NULL;
1098     if (!FcDirCacheProcess (config, dir,
1099                             FcDirCacheMapHelper,
1100                             &cache, cache_file))
1101         cache = NULL;
1102
1103     FcConfigDestroy (config);
1104
1105     return cache;
1106 }
1107
1108 FcCache *
1109 FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
1110 {
1111     int fd;
1112     FcCache *cache = NULL;
1113     struct stat my_file_stat;
1114     FcConfig *config;
1115
1116     if (!file_stat)
1117         file_stat = &my_file_stat;
1118     config = FcConfigReference (NULL);
1119     if (!config)
1120         return NULL;
1121     fd = FcDirCacheOpenFile (cache_file, file_stat);
1122     if (fd >= 0)
1123     {
1124         cache = FcDirCacheMapFd (config, fd, file_stat, NULL);
1125         close (fd);
1126     }
1127     FcConfigDestroy (config);
1128
1129     return cache;
1130 }
1131
1132 static int
1133 FcDirChecksum (struct stat *statb)
1134 {
1135     int                 ret = (int) statb->st_mtime;
1136     char                *endptr;
1137     char                *source_date_epoch;
1138     unsigned long long  epoch;
1139
1140     source_date_epoch = getenv("SOURCE_DATE_EPOCH");
1141     if (source_date_epoch)
1142     {
1143         errno = 0;
1144         epoch = strtoull(source_date_epoch, &endptr, 10);
1145
1146         if (endptr == source_date_epoch)
1147             fprintf (stderr,
1148                      "Fontconfig: SOURCE_DATE_EPOCH invalid\n");
1149         else if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
1150                 || (errno != 0 && epoch == 0))
1151             fprintf (stderr,
1152                      "Fontconfig: SOURCE_DATE_EPOCH: strtoull: %s: %" FC_UINT64_FORMAT "\n",
1153                      strerror(errno), epoch);
1154         else if (*endptr != '\0')
1155             fprintf (stderr,
1156                      "Fontconfig: SOURCE_DATE_EPOCH has trailing garbage\n");
1157         else if (epoch > ULONG_MAX)
1158             fprintf (stderr,
1159                      "Fontconfig: SOURCE_DATE_EPOCH must be <= %lu but saw: %" FC_UINT64_FORMAT "\n",
1160                      ULONG_MAX, epoch);
1161         else if (epoch < ret)
1162             /* Only override if directory is newer */
1163             ret = (int) epoch;
1164     }
1165
1166     return ret;
1167 }
1168
1169 static int64_t
1170 FcDirChecksumNano (struct stat *statb)
1171 {
1172 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1173     /* No nanosecond component to parse */
1174     if (getenv("SOURCE_DATE_EPOCH"))
1175         return 0;
1176     return statb->st_mtim.tv_nsec;
1177 #else
1178     return 0;
1179 #endif
1180 }
1181
1182 /*
1183  * Validate a cache file by reading the header and checking
1184  * the magic number and the size field
1185  */
1186 static FcBool
1187 FcDirCacheValidateHelper (FcConfig *config, int fd, struct stat *fd_stat, struct stat *dir_stat, struct timeval *latest_cache_mtime, void *closure FC_UNUSED)
1188 {
1189     FcBool  ret = FcTrue;
1190     FcCache     c;
1191
1192     if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache))
1193         ret = FcFalse;
1194     else if (c.magic != FC_CACHE_MAGIC_MMAP)
1195         ret = FcFalse;
1196     else if (c.version < FC_CACHE_VERSION_NUMBER)
1197         ret = FcFalse;
1198     else if (fd_stat->st_size != c.size)
1199         ret = FcFalse;
1200     else if (c.checksum != FcDirChecksum (dir_stat))
1201         ret = FcFalse;
1202 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1203     else if (c.checksum_nano != FcDirChecksumNano (dir_stat))
1204         ret = FcFalse;
1205 #endif
1206     return ret;
1207 }
1208
1209 static FcBool
1210 FcDirCacheValidConfig (const FcChar8 *dir, FcConfig *config)
1211 {
1212     return FcDirCacheProcess (config, dir,
1213                               FcDirCacheValidateHelper,
1214                               NULL, NULL);
1215 }
1216
1217 FcBool
1218 FcDirCacheValid (const FcChar8 *dir)
1219 {
1220     FcConfig    *config;
1221     FcBool      ret;
1222
1223     config = FcConfigReference (NULL);
1224     if (!config)
1225         return FcFalse;
1226
1227     ret = FcDirCacheValidConfig (dir, config);
1228     FcConfigDestroy (config);
1229
1230     return ret;
1231 }
1232
1233 /*
1234  * Build a cache structure from the given contents
1235  */
1236 FcCache *
1237 FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcStrSet *dirs)
1238 {
1239     FcSerialize *serialize = FcSerializeCreate ();
1240     FcCache *cache;
1241     int i;
1242     FcChar8     *dir_serialize;
1243     intptr_t    *dirs_serialize;
1244     FcFontSet   *set_serialize;
1245
1246     if (!serialize)
1247         return NULL;
1248     /*
1249      * Space for cache structure
1250      */
1251     FcSerializeReserve (serialize, sizeof (FcCache));
1252     /*
1253      * Directory name
1254      */
1255     if (!FcStrSerializeAlloc (serialize, dir))
1256         goto bail1;
1257     /*
1258      * Subdirs
1259      */
1260     FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *));
1261     for (i = 0; i < dirs->num; i++)
1262         if (!FcStrSerializeAlloc (serialize, dirs->strs[i]))
1263             goto bail1;
1264
1265     /*
1266      * Patterns
1267      */
1268     if (!FcFontSetSerializeAlloc (serialize, set))
1269         goto bail1;
1270
1271     /* Serialize layout complete. Now allocate space and fill it */
1272     cache = malloc (serialize->size);
1273     if (!cache)
1274         goto bail1;
1275     /* shut up valgrind */
1276     memset (cache, 0, serialize->size);
1277
1278     serialize->linear = cache;
1279
1280     cache->magic = FC_CACHE_MAGIC_ALLOC;
1281     cache->version = FC_CACHE_VERSION_NUMBER;
1282     cache->size = serialize->size;
1283     cache->checksum = FcDirChecksum (dir_stat);
1284     cache->checksum_nano = FcDirChecksumNano (dir_stat);
1285
1286     /*
1287      * Serialize directory name
1288      */
1289     dir_serialize = FcStrSerialize (serialize, dir);
1290     if (!dir_serialize)
1291         goto bail2;
1292     cache->dir = FcPtrToOffset (cache, dir_serialize);
1293
1294     /*
1295      * Serialize sub dirs
1296      */
1297     dirs_serialize = FcSerializePtr (serialize, dirs);
1298     if (!dirs_serialize)
1299         goto bail2;
1300     cache->dirs = FcPtrToOffset (cache, dirs_serialize);
1301     cache->dirs_count = dirs->num;
1302     for (i = 0; i < dirs->num; i++)
1303     {
1304         FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]);
1305         if (!d_serialize)
1306             goto bail2;
1307         dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize);
1308     }
1309
1310     /*
1311      * Serialize font set
1312      */
1313     set_serialize = FcFontSetSerialize (serialize, set);
1314     if (!set_serialize)
1315         goto bail2;
1316     cache->set = FcPtrToOffset (cache, set_serialize);
1317
1318     FcSerializeDestroy (serialize);
1319
1320     FcCacheInsert (cache, NULL);
1321
1322     return cache;
1323
1324 bail2:
1325     free (cache);
1326 bail1:
1327     FcSerializeDestroy (serialize);
1328     return NULL;
1329 }
1330
1331 FcCache *
1332 FcDirCacheRebuild (FcCache *cache, struct stat *dir_stat, FcStrSet *dirs)
1333 {
1334     FcCache *new;
1335     FcFontSet *set = FcFontSetDeserialize (FcCacheSet (cache));
1336     const FcChar8 *dir = FcCacheDir (cache);
1337
1338     new = FcDirCacheBuild (set, dir, dir_stat, dirs);
1339     FcFontSetDestroy (set);
1340
1341     return new;
1342 }
1343
1344 /* write serialized state to the cache file */
1345 FcBool
1346 FcDirCacheWrite (FcCache *cache, FcConfig *config)
1347 {
1348     FcChar8         *dir = FcCacheDir (cache);
1349     FcChar8         cache_base[CACHEBASE_LEN];
1350     FcChar8         *cache_hashed;
1351     int             fd;
1352     FcAtomic        *atomic;
1353     FcStrList       *list;
1354     FcChar8         *cache_dir = NULL;
1355     FcChar8         *test_dir, *d = NULL;
1356     FcCacheSkip     *skip;
1357     struct stat     cache_stat;
1358     unsigned int    magic;
1359     int             written;
1360     const FcChar8   *sysroot = FcConfigGetSysRoot (config);
1361
1362     /*
1363      * Write it to the first directory in the list which is writable
1364      */
1365
1366     list = FcStrListCreate (config->cacheDirs);
1367     if (!list)
1368         return FcFalse;
1369     while ((test_dir = FcStrListNext (list)))
1370     {
1371         if (d)
1372             FcStrFree (d);
1373         if (sysroot)
1374             d = FcStrBuildFilename (sysroot, test_dir, NULL);
1375         else
1376             d = FcStrCopyFilename (test_dir);
1377
1378         if (access ((char *) d, W_OK) == 0)
1379         {
1380             cache_dir = FcStrCopyFilename (d);
1381             break;
1382         }
1383         else
1384         {
1385             /*
1386              * If the directory doesn't exist, try to create it
1387              */
1388             if (access ((char *) d, F_OK) == -1) {
1389                 if (FcMakeDirectory (d))
1390                 {
1391                     cache_dir = FcStrCopyFilename (d);
1392                     /* Create CACHEDIR.TAG */
1393                     FcDirCacheCreateTagFile (d);
1394                     break;
1395                 }
1396             }
1397             /*
1398              * Otherwise, try making it writable
1399              */
1400             else if (chmod ((char *) d, 0755) == 0)
1401             {
1402                 cache_dir = FcStrCopyFilename (d);
1403                 /* Try to create CACHEDIR.TAG too */
1404                 FcDirCacheCreateTagFile (d);
1405                 break;
1406             }
1407         }
1408     }
1409     if (!test_dir)
1410         fprintf (stderr, "Fontconfig error: No writable cache directories\n");
1411     if (d)
1412         FcStrFree (d);
1413     FcStrListDone (list);
1414     if (!cache_dir)
1415         return FcFalse;
1416
1417     FcDirCacheBasenameMD5 (config, dir, cache_base);
1418     cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
1419     FcStrFree (cache_dir);
1420     if (!cache_hashed)
1421         return FcFalse;
1422
1423     if (FcDebug () & FC_DBG_CACHE)
1424         printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n",
1425                 dir, cache_hashed);
1426
1427     atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
1428     if (!atomic)
1429         goto bail1;
1430
1431     if (!FcAtomicLock (atomic))
1432         goto bail3;
1433
1434     fd = FcOpen((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
1435     if (fd == -1)
1436         goto bail4;
1437
1438     /* Temporarily switch magic to MMAP while writing to file */
1439     magic = cache->magic;
1440     if (magic != FC_CACHE_MAGIC_MMAP)
1441         cache->magic = FC_CACHE_MAGIC_MMAP;
1442
1443     /*
1444      * Write cache contents to file
1445      */
1446     written = write (fd, cache, cache->size);
1447
1448     /* Switch magic back */
1449     if (magic != FC_CACHE_MAGIC_MMAP)
1450         cache->magic = magic;
1451
1452     if (written != cache->size)
1453     {
1454         perror ("write cache");
1455         goto bail5;
1456     }
1457
1458     close(fd);
1459     if (!FcAtomicReplaceOrig(atomic))
1460         goto bail4;
1461
1462     /* If the file is small, update the cache chain entry such that the
1463      * new cache file is not read again.  If it's large, we don't do that
1464      * such that we reload it, using mmap, which is shared across processes.
1465      */
1466     if (cache->size < FC_CACHE_MIN_MMAP && FcStat (cache_hashed, &cache_stat))
1467     {
1468         lock_cache ();
1469         if ((skip = FcCacheFindByAddrUnlocked (cache)))
1470         {
1471             skip->cache_dev = cache_stat.st_dev;
1472             skip->cache_ino = cache_stat.st_ino;
1473             skip->cache_mtime = cache_stat.st_mtime;
1474 #ifdef HAVE_STRUCT_STAT_ST_MTIM
1475             skip->cache_mtime_nano = cache_stat.st_mtim.tv_nsec;
1476 #else
1477             skip->cache_mtime_nano = 0;
1478 #endif
1479         }
1480         unlock_cache ();
1481     }
1482
1483     FcStrFree (cache_hashed);
1484     FcAtomicUnlock (atomic);
1485     FcAtomicDestroy (atomic);
1486     return FcTrue;
1487
1488  bail5:
1489     close (fd);
1490  bail4:
1491     FcAtomicUnlock (atomic);
1492  bail3:
1493     FcAtomicDestroy (atomic);
1494  bail1:
1495     FcStrFree (cache_hashed);
1496     return FcFalse;
1497 }
1498
1499 FcBool
1500 FcDirCacheClean (const FcChar8 *cache_dir, FcBool verbose)
1501 {
1502     DIR         *d;
1503     struct dirent *ent;
1504     FcChar8     *dir;
1505     FcBool      ret = FcTrue;
1506     FcBool      remove;
1507     FcCache     *cache;
1508     struct stat target_stat;
1509     const FcChar8 *sysroot;
1510     FcConfig    *config;
1511
1512     config = FcConfigReference (NULL);
1513     if (!config)
1514         return FcFalse;
1515     /* FIXME: this API needs to support non-current FcConfig */
1516     sysroot = FcConfigGetSysRoot (config);
1517     if (sysroot)
1518         dir = FcStrBuildFilename (sysroot, cache_dir, NULL);
1519     else
1520         dir = FcStrCopyFilename (cache_dir);
1521     if (!dir)
1522     {
1523         fprintf (stderr, "Fontconfig error: %s: out of memory\n", cache_dir);
1524         ret = FcFalse;
1525         goto bail;
1526     }
1527     if (access ((char *) dir, W_OK) != 0)
1528     {
1529         if (verbose || FcDebug () & FC_DBG_CACHE)
1530             printf ("%s: not cleaning %s cache directory\n", dir,
1531                     access ((char *) dir, F_OK) == 0 ? "unwritable" : "non-existent");
1532         goto bail0;
1533     }
1534     if (verbose || FcDebug () & FC_DBG_CACHE)
1535         printf ("%s: cleaning cache directory\n", dir);
1536     d = opendir ((char *) dir);
1537     if (!d)
1538     {
1539         perror ((char *) dir);
1540         ret = FcFalse;
1541         goto bail0;
1542     }
1543     while ((ent = readdir (d)))
1544     {
1545         FcChar8 *file_name;
1546         const FcChar8   *target_dir;
1547
1548         if (ent->d_name[0] == '.')
1549             continue;
1550         /* skip cache files for different architectures and */
1551         /* files which are not cache files at all */
1552         if (strlen(ent->d_name) != 32 + strlen ("-" FC_ARCHITECTURE FC_CACHE_SUFFIX) ||
1553             strcmp(ent->d_name + 32, "-" FC_ARCHITECTURE FC_CACHE_SUFFIX))
1554             continue;
1555
1556         file_name = FcStrBuildFilename (dir, (FcChar8 *)ent->d_name, NULL);
1557         if (!file_name)
1558         {
1559             fprintf (stderr, "Fontconfig error: %s: allocation failure\n", dir);
1560             ret = FcFalse;
1561             break;
1562         }
1563         remove = FcFalse;
1564         cache = FcDirCacheLoadFile (file_name, NULL);
1565         if (!cache)
1566         {
1567             if (verbose || FcDebug () & FC_DBG_CACHE)
1568                 printf ("%s: invalid cache file: %s\n", dir, ent->d_name);
1569             remove = FcTrue;
1570         }
1571         else
1572         {
1573             FcChar8 *s;
1574
1575             target_dir = FcCacheDir (cache);
1576             if (sysroot)
1577                 s = FcStrBuildFilename (sysroot, target_dir, NULL);
1578             else
1579                 s = FcStrdup (target_dir);
1580             if (stat ((char *) s, &target_stat) < 0)
1581             {
1582                 if (verbose || FcDebug () & FC_DBG_CACHE)
1583                     printf ("%s: %s: missing directory: %s \n",
1584                             dir, ent->d_name, s);
1585                 remove = FcTrue;
1586             }
1587             FcDirCacheUnload (cache);
1588             FcStrFree (s);
1589         }
1590         if (remove)
1591         {
1592             if (unlink ((char *) file_name) < 0)
1593             {
1594                 perror ((char *) file_name);
1595                 ret = FcFalse;
1596             }
1597         }
1598         FcStrFree (file_name);
1599     }
1600
1601     closedir (d);
1602 bail0:
1603     FcStrFree (dir);
1604 bail:
1605     FcConfigDestroy (config);
1606
1607     return ret;
1608 }
1609
1610 int
1611 FcDirCacheLock (const FcChar8 *dir,
1612                 FcConfig      *config)
1613 {
1614     FcChar8 *cache_hashed = NULL;
1615     FcChar8 cache_base[CACHEBASE_LEN];
1616     FcStrList *list;
1617     FcChar8 *cache_dir;
1618     const FcChar8 *sysroot = FcConfigGetSysRoot (config);
1619     int fd = -1;
1620
1621     FcDirCacheBasenameMD5 (config, dir, cache_base);
1622     list = FcStrListCreate (config->cacheDirs);
1623     if (!list)
1624         return -1;
1625
1626     while ((cache_dir = FcStrListNext (list)))
1627     {
1628         if (sysroot)
1629             cache_hashed = FcStrBuildFilename (sysroot, cache_dir, cache_base, NULL);
1630         else
1631             cache_hashed = FcStrBuildFilename (cache_dir, cache_base, NULL);
1632         if (!cache_hashed)
1633             break;
1634         fd = FcOpen ((const char *)cache_hashed, O_RDWR);
1635         FcStrFree (cache_hashed);
1636         /* No caches in that directory. simply retry with another one */
1637         if (fd != -1)
1638         {
1639 #if defined(_WIN32)
1640             if (_locking (fd, _LK_LOCK, 1) == -1)
1641                 goto bail;
1642 #else
1643             struct flock fl;
1644
1645             fl.l_type = F_WRLCK;
1646             fl.l_whence = SEEK_SET;
1647             fl.l_start = 0;
1648             fl.l_len = 0;
1649             fl.l_pid = getpid ();
1650             if (fcntl (fd, F_SETLKW, &fl) == -1)
1651                 goto bail;
1652 #endif
1653             break;
1654         }
1655     }
1656     FcStrListDone (list);
1657     return fd;
1658 bail:
1659     FcStrListDone (list);
1660     if (fd != -1)
1661         close (fd);
1662     return -1;
1663 }
1664
1665 void
1666 FcDirCacheUnlock (int fd)
1667 {
1668     if (fd != -1)
1669     {
1670 #if defined(_WIN32)
1671         _locking (fd, _LK_UNLCK, 1);
1672 #else
1673         struct flock fl;
1674
1675         fl.l_type = F_UNLCK;
1676         fl.l_whence = SEEK_SET;
1677         fl.l_start = 0;
1678         fl.l_len = 0;
1679         fl.l_pid = getpid ();
1680         fcntl (fd, F_SETLK, &fl);
1681 #endif
1682         close (fd);
1683     }
1684 }
1685
1686 /*
1687  * Hokey little macro trick to permit the definitions of C functions
1688  * with the same name as CPP macros
1689  */
1690 #define args1(x)            (x)
1691 #define args2(x,y)          (x,y)
1692
1693 const FcChar8 *
1694 FcCacheDir args1(const FcCache *c)
1695 {
1696     return FcCacheDir (c);
1697 }
1698
1699 FcFontSet *
1700 FcCacheCopySet args1(const FcCache *c)
1701 {
1702     FcFontSet   *old = FcCacheSet (c);
1703     FcFontSet   *new = FcFontSetCreate ();
1704     int         i;
1705
1706     if (!new)
1707         return NULL;
1708     for (i = 0; i < old->nfont; i++)
1709     {
1710         FcPattern   *font = FcFontSetFont (old, i);
1711
1712         FcPatternReference (font);
1713         if (!FcFontSetAdd (new, font))
1714         {
1715             FcFontSetDestroy (new);
1716             return NULL;
1717         }
1718     }
1719     return new;
1720 }
1721
1722 const FcChar8 *
1723 FcCacheSubdir args2(const FcCache *c, int i)
1724 {
1725     return FcCacheSubdir (c, i);
1726 }
1727
1728 int
1729 FcCacheNumSubdir args1(const FcCache *c)
1730 {
1731     return c->dirs_count;
1732 }
1733
1734 int
1735 FcCacheNumFont args1(const FcCache *c)
1736 {
1737     return FcCacheSet(c)->nfont;
1738 }
1739
1740 FcBool
1741 FcDirCacheCreateTagFile (const FcChar8 *cache_dir)
1742 {
1743     FcChar8             *cache_tag;
1744     int                  fd;
1745     FILE                *fp;
1746     FcAtomic            *atomic;
1747     static const FcChar8 cache_tag_contents[] =
1748         "Signature: 8a477f597d28d172789f06886806bc55\n"
1749         "# This file is a cache directory tag created by fontconfig.\n"
1750         "# For information about cache directory tags, see:\n"
1751         "#       http://www.brynosaurus.com/cachedir/\n";
1752     static size_t        cache_tag_contents_size = sizeof (cache_tag_contents) - 1;
1753     FcBool               ret = FcFalse;
1754
1755     if (!cache_dir)
1756         return FcFalse;
1757
1758     if (access ((char *) cache_dir, W_OK) == 0)
1759     {
1760         /* Create CACHEDIR.TAG */
1761         cache_tag = FcStrBuildFilename (cache_dir, "CACHEDIR.TAG", NULL);
1762         if (!cache_tag)
1763             return FcFalse;
1764         atomic = FcAtomicCreate ((FcChar8 *)cache_tag);
1765         if (!atomic)
1766             goto bail1;
1767         if (!FcAtomicLock (atomic))
1768             goto bail2;
1769         fd = FcOpen((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT, 0644);
1770         if (fd == -1)
1771             goto bail3;
1772         fp = fdopen(fd, "wb");
1773         if (fp == NULL)
1774             goto bail3;
1775
1776         fwrite(cache_tag_contents, cache_tag_contents_size, sizeof (FcChar8), fp);
1777         fclose(fp);
1778
1779         if (!FcAtomicReplaceOrig(atomic))
1780             goto bail3;
1781
1782         ret = FcTrue;
1783       bail3:
1784         FcAtomicUnlock (atomic);
1785       bail2:
1786         FcAtomicDestroy (atomic);
1787       bail1:
1788         FcStrFree (cache_tag);
1789     }
1790
1791     if (FcDebug () & FC_DBG_CACHE)
1792     {
1793         if (ret)
1794             printf ("Created CACHEDIR.TAG at %s\n", cache_dir);
1795         else
1796             printf ("Unable to create CACHEDIR.TAG at %s\n", cache_dir);
1797     }
1798
1799     return ret;
1800 }
1801
1802 void
1803 FcCacheCreateTagFile (FcConfig *config)
1804 {
1805     FcChar8   *cache_dir = NULL, *d = NULL;
1806     FcStrList *list;
1807     const FcChar8 *sysroot;
1808
1809     config = FcConfigReference (config);
1810     if (!config)
1811         return;
1812     sysroot = FcConfigGetSysRoot (config);
1813
1814     list = FcConfigGetCacheDirs (config);
1815     if (!list)
1816         goto bail;
1817
1818     while ((cache_dir = FcStrListNext (list)))
1819     {
1820         if (d)
1821             FcStrFree (d);
1822         if (sysroot)
1823             d = FcStrBuildFilename (sysroot, cache_dir, NULL);
1824         else
1825             d = FcStrCopyFilename (cache_dir);
1826         if (FcDirCacheCreateTagFile (d))
1827             break;
1828     }
1829     if (d)
1830         FcStrFree (d);
1831     FcStrListDone (list);
1832 bail:
1833     FcConfigDestroy (config);
1834 }
1835
1836 #define __fccache__
1837 #include "fcaliastail.h"
1838 #undef __fccache__