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