(root)/
fontconfig-2.14.2/
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__