| 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 | /* No caches in that directory. simply retry with another one */ | |||
| 1177 | if (fd != -1) | |||
| 1178 | { | |||
| 1179 | #if defined(_WIN32) | |||
| 1180 | if (_locking (fd, _LK_LOCK, 1) == -1) | |||
| 1181 | goto bail; | |||
| 1182 | #else | |||
| 1183 | struct flock fl; | |||
| 1184 | ||||
| 1185 | fl.l_type = F_WRLCK3; | |||
| 1186 | fl.l_whence = SEEK_SET0; | |||
| 1187 | fl.l_start = 0; | |||
| 1188 | fl.l_len = 0; | |||
| 1189 | fl.l_pid = getpid (); | |||
| 1190 | if (fcntl (fd, F_SETLKW9, &fl) == -1) | |||
| 1191 | goto bail; | |||
| 1192 | #endif | |||
| 1193 | break; | |||
| 1194 | } | |||
| 1195 | } | |||
| 1196 | FcStrListDone (list); | |||
| 1197 | return fd; | |||
| 1198 | bail: | |||
| 1199 | if (fd != -1) | |||
| 1200 | close (fd); | |||
| 1201 | return -1; | |||
| 1202 | } | |||
| 1203 | ||||
| 1204 | void | |||
| 1205 | FcDirCacheUnlock (int fd) | |||
| 1206 | { | |||
| 1207 | if (fd != -1) | |||
| 1208 | { | |||
| 1209 | #if defined(_WIN32) | |||
| 1210 | _locking (fd, _LK_UNLCK, 1); | |||
| 1211 | #else | |||
| 1212 | struct flock fl; | |||
| 1213 | ||||
| 1214 | fl.l_type = F_UNLCK2; | |||
| 1215 | fl.l_whence = SEEK_SET0; | |||
| 1216 | fl.l_start = 0; | |||
| 1217 | fl.l_len = 0; | |||
| 1218 | fl.l_pid = getpid (); | |||
| 1219 | fcntl (fd, F_SETLK8, &fl); | |||
| 1220 | #endif | |||
| 1221 | close (fd); | |||
| 1222 | } | |||
| 1223 | } | |||
| 1224 | ||||
| 1225 | /* | |||
| 1226 | * Hokey little macro trick to permit the definitions of C functions | |||
| 1227 | * with the same name as CPP macros | |||
| 1228 | */ | |||
| 1229 | #define args1(x)(x) (x) | |||
| 1230 | #define args2(x,y)(x,y) (x,y) | |||
| 1231 | ||||
| 1232 | const FcChar8 * | |||
| 1233 | FcCacheDir args1(const FcCache *c)(const FcCache *c) | |||
| 1234 | { | |||
| 1235 | return FcCacheDir (c)((FcChar8 *) ((intptr_t) (c) + ((c)->dir))); | |||
| 1236 | } | |||
| 1237 | ||||
| 1238 | FcFontSet * | |||
| 1239 | FcCacheCopySet args1(const FcCache *c)(const FcCache *c) | |||
| 1240 | { | |||
| 1241 | FcFontSet *old = FcCacheSet (c)((FcFontSet *) ((intptr_t) (c) + ((c)->set))); | |||
| 1242 | FcFontSet *new = FcFontSetCreate (); | |||
| 1243 | int i; | |||
| 1244 | ||||
| 1245 | if (!new) | |||
| 1246 | return NULL((void*)0); | |||
| 1247 | for (i = 0; i < old->nfont; i++) | |||
| 1248 | { | |||
| 1249 | 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]); | |||
| 1250 | ||||
| 1251 | FcPatternReference (font); | |||
| 1252 | if (!FcFontSetAdd (new, font)) | |||
| 1253 | { | |||
| 1254 | FcFontSetDestroy (new); | |||
| 1255 | return NULL((void*)0); | |||
| 1256 | } | |||
| 1257 | } | |||
| 1258 | return new; | |||
| 1259 | } | |||
| 1260 | ||||
| 1261 | const FcChar8 * | |||
| 1262 | FcCacheSubdir args2(const FcCache *c, int i)(const FcCache *c,int i) | |||
| 1263 | { | |||
| 1264 | return FcCacheSubdir (c, i)((FcChar8 *) ((intptr_t) (((intptr_t *) ((intptr_t) (c) + ((c )->dirs)))) + (((intptr_t *) ((intptr_t) (c) + ((c)->dirs )))[i]))); | |||
| 1265 | } | |||
| 1266 | ||||
| 1267 | int | |||
| 1268 | FcCacheNumSubdir args1(const FcCache *c)(const FcCache *c) | |||
| 1269 | { | |||
| 1270 | return c->dirs_count; | |||
| 1271 | } | |||
| 1272 | ||||
| 1273 | int | |||
| 1274 | FcCacheNumFont args1(const FcCache *c)(const FcCache *c) | |||
| 1275 | { | |||
| 1276 | return FcCacheSet(c)((FcFontSet *) ((intptr_t) (c) + ((c)->set)))->nfont; | |||
| 1277 | } | |||
| 1278 | ||||
| 1279 | /* | |||
| 1280 | * This code implements the MD5 message-digest algorithm. | |||
| 1281 | * The algorithm is due to Ron Rivest. This code was | |||
| 1282 | * written by Colin Plumb in 1993, no copyright is claimed. | |||
| 1283 | * This code is in the public domain; do with it what you wish. | |||
| 1284 | * | |||
| 1285 | * Equivalent code is available from RSA Data Security, Inc. | |||
| 1286 | * This code has been tested against that, and is equivalent, | |||
| 1287 | * except that you don't need to include two pages of legalese | |||
| 1288 | * with every copy. | |||
| 1289 | * | |||
| 1290 | * To compute the message digest of a chunk of bytes, declare an | |||
| 1291 | * MD5Context structure, pass it to MD5Init, call MD5Update as | |||
| 1292 | * needed on buffers full of bytes, and then call MD5Final, which | |||
| 1293 | * will fill a supplied 16-byte array with the digest. | |||
| 1294 | */ | |||
| 1295 | ||||
| 1296 | #ifndef HIGHFIRST | |||
| 1297 | #define byteReverse(buf, len) /* Nothing */ | |||
| 1298 | #else | |||
| 1299 | /* | |||
| 1300 | * Note: this code is harmless on little-endian machines. | |||
| 1301 | */ | |||
| 1302 | void byteReverse(unsigned char *buf, unsigned longs) | |||
| 1303 | { | |||
| 1304 | FcChar32 t; | |||
| 1305 | do { | |||
| 1306 | t = (FcChar32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | | |||
| 1307 | ((unsigned) buf[1] << 8 | buf[0]); | |||
| 1308 | *(FcChar32 *) buf = t; | |||
| 1309 | buf += 4; | |||
| 1310 | } while (--longs); | |||
| 1311 | } | |||
| 1312 | #endif | |||
| 1313 | ||||
| 1314 | /* | |||
| 1315 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious | |||
| 1316 | * initialization constants. | |||
| 1317 | */ | |||
| 1318 | static void MD5Init(struct MD5Context *ctx) | |||
| 1319 | { | |||
| 1320 | ctx->buf[0] = 0x67452301; | |||
| 1321 | ctx->buf[1] = 0xefcdab89; | |||
| 1322 | ctx->buf[2] = 0x98badcfe; | |||
| 1323 | ctx->buf[3] = 0x10325476; | |||
| 1324 | ||||
| 1325 | ctx->bits[0] = 0; | |||
| 1326 | ctx->bits[1] = 0; | |||
| 1327 | } | |||
| 1328 | ||||
| 1329 | /* | |||
| 1330 | * Update context to reflect the concatenation of another buffer full | |||
| 1331 | * of bytes. | |||
| 1332 | */ | |||
| 1333 | static void MD5Update(struct MD5Context *ctx, const unsigned char *buf, unsigned len) | |||
| 1334 | { | |||
| 1335 | FcChar32 t; | |||
| 1336 | ||||
| 1337 | /* Update bitcount */ | |||
| 1338 | ||||
| 1339 | t = ctx->bits[0]; | |||
| 1340 | if ((ctx->bits[0] = t + ((FcChar32) len << 3)) < t) | |||
| 1341 | ctx->bits[1]++; /* Carry from low to high */ | |||
| 1342 | ctx->bits[1] += len >> 29; | |||
| 1343 | ||||
| 1344 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ | |||
| 1345 | ||||
| 1346 | /* Handle any leading odd-sized chunks */ | |||
| 1347 | ||||
| 1348 | if (t) { | |||
| 1349 | unsigned char *p = (unsigned char *) ctx->in + t; | |||
| 1350 | ||||
| 1351 | t = 64 - t; | |||
| 1352 | if (len < t) { | |||
| 1353 | memcpy(p, buf, len)__builtin___memcpy_chk (p, buf, len, __builtin_object_size (p , 0)); | |||
| 1354 | return; | |||
| 1355 | } | |||
| 1356 | memcpy(p, buf, t)__builtin___memcpy_chk (p, buf, t, __builtin_object_size (p, 0 )); | |||
| 1357 | byteReverse(ctx->in, 16); | |||
| 1358 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | |||
| 1359 | buf += t; | |||
| 1360 | len -= t; | |||
| 1361 | } | |||
| 1362 | /* Process data in 64-byte chunks */ | |||
| 1363 | ||||
| 1364 | while (len >= 64) { | |||
| 1365 | memcpy(ctx->in, buf, 64)__builtin___memcpy_chk (ctx->in, buf, 64, __builtin_object_size (ctx->in, 0)); | |||
| 1366 | byteReverse(ctx->in, 16); | |||
| 1367 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | |||
| 1368 | buf += 64; | |||
| 1369 | len -= 64; | |||
| 1370 | } | |||
| 1371 | ||||
| 1372 | /* Handle any remaining bytes of data. */ | |||
| 1373 | ||||
| 1374 | memcpy(ctx->in, buf, len)__builtin___memcpy_chk (ctx->in, buf, len, __builtin_object_size (ctx->in, 0)); | |||
| 1375 | } | |||
| 1376 | ||||
| 1377 | /* | |||
| 1378 | * Final wrapup - pad to 64-byte boundary with the bit pattern | |||
| 1379 | * 1 0* (64-bit count of bits processed, MSB-first) | |||
| 1380 | */ | |||
| 1381 | static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) | |||
| 1382 | { | |||
| 1383 | unsigned count; | |||
| 1384 | unsigned char *p; | |||
| 1385 | ||||
| 1386 | /* Compute number of bytes mod 64 */ | |||
| 1387 | count = (ctx->bits[0] >> 3) & 0x3F; | |||
| 1388 | ||||
| 1389 | /* Set the first char of padding to 0x80. This is safe since there is | |||
| 1390 | always at least one byte free */ | |||
| 1391 | p = ctx->in + count; | |||
| 1392 | *p++ = 0x80; | |||
| 1393 | ||||
| 1394 | /* Bytes of padding needed to make 64 bytes */ | |||
| 1395 | count = 64 - 1 - count; | |||
| 1396 | ||||
| 1397 | /* Pad out to 56 mod 64 */ | |||
| 1398 | if (count < 8) { | |||
| 1399 | /* Two lots of padding: Pad the first block to 64 bytes */ | |||
| 1400 | memset(p, 0, count)__builtin___memset_chk (p, 0, count, __builtin_object_size (p , 0)); | |||
| 1401 | byteReverse(ctx->in, 16); | |||
| 1402 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | |||
| 1403 | ||||
| 1404 | /* Now fill the next block with 56 bytes */ | |||
| 1405 | memset(ctx->in, 0, 56)__builtin___memset_chk (ctx->in, 0, 56, __builtin_object_size (ctx->in, 0)); | |||
| 1406 | } else { | |||
| 1407 | /* Pad block to 56 bytes */ | |||
| 1408 | memset(p, 0, count - 8)__builtin___memset_chk (p, 0, count - 8, __builtin_object_size (p, 0)); | |||
| 1409 | } | |||
| 1410 | byteReverse(ctx->in, 14); | |||
| 1411 | ||||
| 1412 | /* Append length in bits and transform */ | |||
| 1413 | ((FcChar32 *) ctx->in)[14] = ctx->bits[0]; | |||
| 1414 | ((FcChar32 *) ctx->in)[15] = ctx->bits[1]; | |||
| 1415 | ||||
| 1416 | MD5Transform(ctx->buf, (FcChar32 *) ctx->in); | |||
| 1417 | byteReverse((unsigned char *) ctx->buf, 4); | |||
| 1418 | memcpy(digest, ctx->buf, 16)__builtin___memcpy_chk (digest, ctx->buf, 16, __builtin_object_size (digest, 0)); | |||
| 1419 | memset(ctx, 0, sizeof(*ctx))__builtin___memset_chk (ctx, 0, sizeof(*ctx), __builtin_object_size (ctx, 0)); /* In case it's sensitive */ | |||
| 1420 | } | |||
| 1421 | ||||
| 1422 | ||||
| 1423 | /* The four core functions - F1 is optimized somewhat */ | |||
| 1424 | ||||
| 1425 | /* #define F1(x, y, z) (x & y | ~x & z) */ | |||
| 1426 | #define F1(x, y, z)(z ^ (x & (y ^ z))) (z ^ (x & (y ^ z))) | |||
| 1427 | #define F2(x, y, z)(y ^ (z & (x ^ y))) F1(z, x, y)(y ^ (z & (x ^ y))) | |||
| 1428 | #define F3(x, y, z)(x ^ y ^ z) (x ^ y ^ z) | |||
| 1429 | #define F4(x, y, z)(y ^ (x | ~z)) (y ^ (x | ~z)) | |||
| 1430 | ||||
| 1431 | /* This is the central step in the MD5 algorithm. */ | |||
| 1432 | #define MD5STEP(f, w, x, y, z, data, s)( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) \ | |||
| 1433 | ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) | |||
| 1434 | ||||
| 1435 | /* | |||
| 1436 | * The core of the MD5 algorithm, this alters an existing MD5 hash to | |||
| 1437 | * reflect the addition of 16 longwords of new data. MD5Update blocks | |||
| 1438 | * the data and converts bytes into longwords for this routine. | |||
| 1439 | */ | |||
| 1440 | static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]) | |||
| 1441 | { | |||
| 1442 | register FcChar32 a, b, c, d; | |||
| 1443 | ||||
| 1444 | a = buf[0]; | |||
| 1445 | b = buf[1]; | |||
| 1446 | c = buf[2]; | |||
| 1447 | d = buf[3]; | |||
| 1448 | ||||
| 1449 | 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 ); | |||
| 1450 | 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 ); | |||
| 1451 | 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 ); | |||
| 1452 | 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 ); | |||
| 1453 | 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 ); | |||
| 1454 | 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 ); | |||
| 1455 | 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 ); | |||
| 1456 | 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 ); | |||
| 1457 | 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 ); | |||
| 1458 | 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 ); | |||
| 1459 | 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 ); | |||
| 1460 | 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 ); | |||
| 1461 | 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 ); | |||
| 1462 | 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 ); | |||
| 1463 | 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 ); | |||
| 1464 | 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 ); | |||
| 1465 | ||||
| 1466 | 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 ); | |||
| 1467 | 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 ); | |||
| 1468 | 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 ); | |||
| 1469 | 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 ); | |||
| 1470 | 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 ); | |||
| 1471 | 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 ); | |||
| 1472 | 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 ); | |||
| 1473 | 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 ); | |||
| 1474 | 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 ); | |||
| 1475 | 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 ); | |||
| 1476 | 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 ); | |||
| 1477 | 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 ); | |||
| 1478 | 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 ); | |||
| 1479 | 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 ); | |||
| 1480 | 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 ); | |||
| 1481 | 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 ); | |||
| 1482 | ||||
| 1483 | 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 ); | |||
| 1484 | 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 ); | |||
| 1485 | 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 ); | |||
| 1486 | 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 ); | |||
| 1487 | 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 ); | |||
| 1488 | 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 ); | |||
| 1489 | 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 ); | |||
| 1490 | 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 ); | |||
| 1491 | 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 ); | |||
| 1492 | 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 ); | |||
| 1493 | 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 ); | |||
| 1494 | 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 ); | |||
| 1495 | 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 ); | |||
| 1496 | 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 ); | |||
| 1497 | 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 ); | |||
| 1498 | 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 ); | |||
| 1499 | ||||
| 1500 | 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 ); | |||
| 1501 | 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 ); | |||
| 1502 | 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 ); | |||
| 1503 | 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 ); | |||
| 1504 | 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 ); | |||
| 1505 | 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 ); | |||
| 1506 | 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 ); | |||
| 1507 | 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 ); | |||
| 1508 | 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 ); | |||
| 1509 | 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 ); | |||
| 1510 | 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 ); | |||
| 1511 | 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 ); | |||
| 1512 | 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 ); | |||
| 1513 | 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 ); | |||
| 1514 | 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 ); | |||
| 1515 | 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 ); | |||
| 1516 | ||||
| 1517 | buf[0] += a; | |||
| 1518 | buf[1] += b; | |||
| 1519 | buf[2] += c; | |||
| 1520 | buf[3] += d; | |||
| 1521 | } | |||
| 1522 | ||||
| 1523 | FcBool | |||
| 1524 | FcDirCacheCreateTagFile (const FcChar8 *cache_dir) | |||
| 1525 | { | |||
| 1526 | FcChar8 *cache_tag; | |||
| 1527 | int fd; | |||
| 1528 | FILE *fp; | |||
| 1529 | FcAtomic *atomic; | |||
| 1530 | static const FcChar8 cache_tag_contents[] = | |||
| 1531 | "Signature: 8a477f597d28d172789f06886806bc55\n" | |||
| 1532 | "# This file is a cache directory tag created by fontconfig.\n" | |||
| 1533 | "# For information about cache directory tags, see:\n" | |||
| 1534 | "# http://www.brynosaurus.com/cachedir/\n"; | |||
| 1535 | static size_t cache_tag_contents_size = sizeof (cache_tag_contents) - 1; | |||
| 1536 | FcBool ret = FcFalse0; | |||
| 1537 | ||||
| 1538 | if (!cache_dir) | |||
| 1539 | return FcFalse0; | |||
| 1540 | ||||
| 1541 | if (access ((char *) cache_dir, W_OK(1<<1)) == 0) | |||
| 1542 | { | |||
| 1543 | /* Create CACHEDIR.TAG */ | |||
| 1544 | cache_tag = FcStrBuildFilename (cache_dir, "CACHEDIR.TAG", NULL((void*)0)); | |||
| 1545 | if (!cache_tag) | |||
| 1546 | return FcFalse0; | |||
| 1547 | atomic = FcAtomicCreate ((FcChar8 *)cache_tag); | |||
| 1548 | if (!atomic) | |||
| 1549 | goto bail1; | |||
| 1550 | if (!FcAtomicLock (atomic)) | |||
| 1551 | goto bail2; | |||
| 1552 | fd = FcOpen((char *)FcAtomicNewFile (atomic), O_RDWR0x0002 | O_CREAT0x0200, 0644); | |||
| 1553 | if (fd == -1) | |||
| 1554 | goto bail3; | |||
| 1555 | fp = fdopen(fd, "wb"); | |||
| 1556 | if (fp == NULL((void*)0)) | |||
| 1557 | goto bail3; | |||
| 1558 | ||||
| 1559 | fwrite(cache_tag_contents, cache_tag_contents_size, sizeof (FcChar8), fp); | |||
| 1560 | fclose(fp); | |||
| 1561 | ||||
| 1562 | if (!FcAtomicReplaceOrig(atomic)) | |||
| 1563 | goto bail3; | |||
| 1564 | ||||
| 1565 | ret = FcTrue1; | |||
| 1566 | bail3: | |||
| 1567 | FcAtomicUnlock (atomic); | |||
| 1568 | bail2: | |||
| 1569 | FcAtomicDestroy (atomic); | |||
| 1570 | bail1: | |||
| 1571 | FcStrFree (cache_tag); | |||
| 1572 | } | |||
| 1573 | ||||
| 1574 | if (FcDebug ()(FcDebugVal) & FC_DBG_CACHE16) | |||
| 1575 | { | |||
| 1576 | if (ret) | |||
| 1577 | printf ("Created CACHEDIR.TAG at %s\n", cache_dir); | |||
| 1578 | else | |||
| 1579 | printf ("Unable to create CACHEDIR.TAG at %s\n", cache_dir); | |||
| 1580 | } | |||
| 1581 | ||||
| 1582 | return ret; | |||
| 1583 | } | |||
| 1584 | ||||
| 1585 | void | |||
| 1586 | FcCacheCreateTagFile (const FcConfig *config) | |||
| 1587 | { | |||
| 1588 | FcChar8 *cache_dir = NULL((void*)0), *d = NULL((void*)0); | |||
| 1589 | FcStrList *list; | |||
| 1590 | const FcChar8 *sysroot = FcConfigGetSysRoot (config); | |||
| 1591 | ||||
| 1592 | list = FcConfigGetCacheDirs (config); | |||
| 1593 | if (!list) | |||
| 1594 | return; | |||
| 1595 | ||||
| 1596 | while ((cache_dir = FcStrListNext (list))) | |||
| 1597 | { | |||
| 1598 | if (d) | |||
| 1599 | FcStrFree (d); | |||
| 1600 | if (sysroot) | |||
| 1601 | d = FcStrBuildFilename (sysroot, cache_dir, NULL((void*)0)); | |||
| 1602 | else | |||
| 1603 | d = FcStrCopyFilename (cache_dir); | |||
| 1604 | if (FcDirCacheCreateTagFile (d)) | |||
| 1605 | break; | |||
| 1606 | } | |||
| 1607 | if (d) | |||
| 1608 | FcStrFree (d); | |||
| 1609 | FcStrListDone (list); | |||
| 1610 | } | |||
| 1611 | ||||
| 1612 | #define __fccache__ | |||
| 1613 | #include "fcaliastail.h" | |||
| 1614 | #undef __fccache__ |