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