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