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