2 * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
4 * Copyright © 2000 Keith Packard
5 * Copyright © 2005 Patrick Lam
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of Keith Packard not be used in
12 * advertising or publicity pertaining to distribution of the software without
13 * specific, written prior permission. Keith Packard makes no
14 * representations about the suitability of this software for any purpose. It
15 * is provided "as is" without express or implied warranty.
17 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
29 #include <sys/utsname.h>
30 #include <sys/types.h>
34 #define ENDIAN_TEST 0x12345678
35 #define MACHINE_SIGNATURE_SIZE 9 + 5*19 + 1
38 FcCacheSkipToArch (int fd, const char * arch);
41 FcCacheCopyOld (int fd, int fd_orig, off_t start);
44 FcDirCacheProduce (FcFontSet *set, FcCache * metadata);
47 FcDirCacheConsume (int fd, const char * dir, FcFontSet *set);
50 FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir);
53 FcCacheNextOffset(off_t w);
56 FcCacheMachineSignature (void);
59 FcCacheHaveBank (int bank);
62 FcCacheAddBankDir (int bank, const char * dir);
64 #define FC_DBG_CACHE_REF 1024
67 FcCacheReadString (int fd, char *dest, int len)
80 while (read (fd, &c, 1) == 1)
107 FcCacheWriteString (int fd, const char *chars)
109 if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
115 FcGlobalCacheDirDestroy (FcGlobalCacheDir *d)
117 FcMemFree (FC_MEM_STRING, strlen (d->name)+1);
119 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCacheDir));
124 FcGlobalCacheCreate (void)
126 FcGlobalCache *cache;
128 cache = malloc (sizeof (FcGlobalCache));
131 FcMemAlloc (FC_MEM_CACHE, sizeof (FcGlobalCache));
133 cache->updated = FcFalse;
139 FcGlobalCacheDestroy (FcGlobalCache *cache)
141 FcGlobalCacheDir *d, *next;
143 for (d = cache->dirs; d; d = next)
146 FcGlobalCacheDirDestroy (d);
148 FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
153 FcGlobalCacheLoad (FcGlobalCache *cache,
155 const FcChar8 *cache_file)
158 FcGlobalCacheDir *d, *next;
159 char * current_arch_machine_name;
160 char candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9];
161 off_t current_arch_start;
163 struct stat cache_stat, dir_stat;
165 if (stat ((char *) cache_file, &cache_stat) < 0)
168 cache->fd = open ((char *) cache_file, O_RDONLY);
172 cache->updated = FcFalse;
174 current_arch_machine_name = FcCacheMachineSignature ();
175 current_arch_start = FcCacheSkipToArch(cache->fd,
176 current_arch_machine_name);
177 if (current_arch_start < 0)
178 goto bail_and_destroy;
180 lseek (cache->fd, current_arch_start, SEEK_SET);
181 FcCacheReadString (cache->fd, candidate_arch_machine_name,
182 sizeof (candidate_arch_machine_name));
183 if (strlen(candidate_arch_machine_name) == 0)
184 goto bail_and_destroy;
190 FcCacheReadString (cache->fd, name_buf, sizeof (name_buf));
191 if (!strlen(name_buf))
194 if (stat ((char *) name_buf, &dir_stat) < 0 ||
195 dir_stat.st_mtime > cache_stat.st_mtime)
199 FcStrSetAdd (staleDirs, FcStrCopy ((FcChar8 *)name_buf));
200 read (cache->fd, &md, sizeof (FcCache));
201 lseek (cache->fd, FcCacheNextOffset (lseek(cache->fd, 0, SEEK_CUR)) + md.count, SEEK_SET);
205 d = malloc (sizeof (FcGlobalCacheDir));
209 d->next = cache->dirs;
212 d->name = (char *)FcStrCopy ((FcChar8 *)name_buf);
214 d->offset = lseek (cache->fd, 0, SEEK_CUR);
215 if (read (cache->fd, &d->metadata, sizeof (FcCache)) != sizeof (FcCache))
217 targ = FcCacheNextOffset (lseek(cache->fd, 0, SEEK_CUR)) + d->metadata.count;
218 if (lseek (cache->fd, targ, SEEK_SET) != targ)
224 for (d = cache->dirs; d; d = next)
239 if (stat ((char *) cache_file, &cache_stat) == 0)
240 unlink ((char *)cache_file);
247 FcGlobalCacheReadDir (FcFontSet *set, FcStrSet *dirs, FcGlobalCache * cache, const char *dir, FcConfig *config)
250 FcBool ret = FcFalse;
255 for (d = cache->dirs; d; d = d->next)
257 if (strncmp (d->name, dir, strlen(dir)) == 0)
259 lseek (cache->fd, d->offset, SEEK_SET);
260 if (!FcDirCacheConsume (cache->fd, dir, set))
262 if (strcmp (d->name, dir) == 0)
271 FcGlobalCacheUpdate (FcGlobalCache *cache,
275 FcGlobalCacheDir * d;
280 for (d = cache->dirs; d; d = d->next)
282 if (strcmp(d->name, name) == 0)
288 d = malloc (sizeof (FcGlobalCacheDir));
291 d->next = cache->dirs;
295 cache->updated = FcTrue;
297 d->name = (char *)FcStrCopy ((FcChar8 *)name);
298 d->ent = FcDirCacheProduce (set, &d->metadata);
304 FcGlobalCacheSave (FcGlobalCache *cache,
305 const FcChar8 *cache_file)
308 FcGlobalCacheDir *dir;
310 off_t current_arch_start = 0, truncate_to;
311 char * current_arch_machine_name, * header;
316 #if defined (HAVE_GETUID) && defined (HAVE_GETEUID)
317 /* Set-UID programs can't safely update the cache */
318 if (getuid () != geteuid ())
322 atomic = FcAtomicCreate (cache_file);
326 if (!FcAtomicLock (atomic))
328 fd = open ((char *) FcAtomicNewFile(atomic), O_RDWR | O_CREAT,
333 fd_orig = open ((char *) FcAtomicOrigFile(atomic), O_RDONLY);
335 current_arch_machine_name = FcCacheMachineSignature ();
337 current_arch_start = 0;
339 current_arch_start = FcCacheSkipToArch (fd_orig,
340 current_arch_machine_name);
342 if (current_arch_start < 0)
343 current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
345 if (!FcCacheCopyOld(fd, fd_orig, current_arch_start))
351 current_arch_start = lseek(fd, 0, SEEK_CUR);
352 if (ftruncate (fd, current_arch_start) == -1)
355 header = malloc (10 + strlen (current_arch_machine_name));
359 truncate_to = current_arch_start + strlen(current_arch_machine_name) + 11;
360 for (dir = cache->dirs; dir; dir = dir->next)
362 truncate_to += strlen(dir->name) + 1;
363 truncate_to += sizeof (FcCache);
364 truncate_to = FcCacheNextOffset (current_arch_start + truncate_to);
365 truncate_to += dir->metadata.count;
367 truncate_to -= current_arch_start;
369 sprintf (header, "%8x ", (int)truncate_to);
370 strcat (header, current_arch_machine_name);
371 if (!FcCacheWriteString (fd, header))
374 for (dir = cache->dirs; dir; dir = dir->next)
378 FcCacheWriteString (fd, dir->name);
379 write (fd, &dir->metadata, sizeof(FcCache));
380 lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_CUR)), SEEK_SET);
381 write (fd, dir->ent, dir->metadata.count);
385 FcCacheWriteString (fd, "");
387 if (close (fd) == -1)
390 if (!FcAtomicReplaceOrig (atomic))
393 FcAtomicUnlock (atomic);
394 FcAtomicDestroy (atomic);
396 cache->updated = FcFalse;
407 FcAtomicDeleteNew (atomic);
409 FcAtomicUnlock (atomic);
411 FcAtomicDestroy (atomic);
415 #define PAGESIZE 8192
417 * Find the next presumably-mmapable offset after the supplied file
421 FcCacheNextOffset(off_t w)
423 if (w % PAGESIZE == 0)
426 return ((w / PAGESIZE)+1)*PAGESIZE;
429 /* return the address of the segment for the provided arch,
430 * or -1 if arch not found */
432 FcCacheSkipToArch (int fd, const char * arch)
434 char candidate_arch_machine_name_count[MACHINE_SIGNATURE_SIZE + 9];
435 char * candidate_arch;
436 off_t current_arch_start = 0;
438 /* skip arches that are not the current arch */
443 if (lseek (fd, current_arch_start, SEEK_SET) != current_arch_start)
446 if (FcCacheReadString (fd, candidate_arch_machine_name_count,
447 sizeof (candidate_arch_machine_name_count)) == 0)
449 if (!strlen(candidate_arch_machine_name_count))
451 bs = strtol(candidate_arch_machine_name_count, &candidate_arch, 16);
453 // count = 0 should probably be distinguished from the !bs condition
454 if (!bs || bs < strlen (candidate_arch_machine_name_count))
457 candidate_arch++; /* skip leading space */
459 if (strcmp (candidate_arch, arch)==0)
460 return current_arch_start;
461 current_arch_start += bs;
467 /* Cuts out the segment at the file pointer (moves everything else
468 * down to cover it), and leaves the file pointer at the end of the
471 FcCacheCopyOld (int fd, int fd_orig, off_t start)
473 char * buf = malloc (8192);
474 char candidate_arch_machine_name[MACHINE_SIGNATURE_SIZE + 9];
476 int c, bytes_skipped;
483 lseek (fd, 0, SEEK_SET); lseek (fd_orig, 0, SEEK_SET);
490 if ((c = read (fd_orig, buf, b)) <= 0)
492 if (write (fd, buf, c) < 0)
499 lseek (fd, start, SEEK_SET);
500 if (FcCacheReadString (fd, candidate_arch_machine_name,
501 sizeof (candidate_arch_machine_name)) == 0)
503 if (!strlen(candidate_arch_machine_name))
506 bs = strtol(candidate_arch_machine_name, 0, 16);
513 lseek (fd, start+bs+bytes_skipped, SEEK_SET);
514 if ((c = read (fd, buf, 8192)) <= 0)
516 lseek (fd, start+bytes_skipped, SEEK_SET);
517 if (write (fd, buf, c) < 0)
522 lseek (fd, start+bytes_skipped, SEEK_SET);
533 /* Does not check that the cache has the appropriate arch section. */
535 FcDirCacheValid (const FcChar8 *dir)
537 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
538 struct stat file_stat, dir_stat;
540 if (stat ((char *) dir, &dir_stat) < 0)
542 FcStrFree (cache_file);
545 if (stat ((char *) cache_file, &file_stat) < 0)
547 FcStrFree (cache_file);
551 FcStrFree (cache_file);
553 * If the directory has been modified more recently than
554 * the cache file, the cache is not valid
556 if (dir_stat.st_mtime - file_stat.st_mtime > 0)
561 /* Assumes that the cache file in 'dir' exists.
562 * Checks that the cache has the appropriate arch section. */
564 FcDirCacheHasCurrentArch (const FcChar8 *dir)
566 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
568 off_t current_arch_start;
569 char *current_arch_machine_name;
571 current_arch_machine_name = FcCacheMachineSignature();
572 fd = open ((char *)cache_file, O_RDONLY);
576 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
579 if (current_arch_start < 0)
586 FcDirCacheUnlink (const FcChar8 *dir)
588 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
589 struct stat cache_stat;
591 if (stat ((char *) cache_file, &cache_stat) == 0 &&
592 unlink ((char *)cache_file) != 0)
594 FcStrFree (cache_file);
598 FcStrFree (cache_file);
603 FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache,
604 FcStrList *list, FcFontSet * set)
608 FcChar8 *file, *base;
614 * Read in the results from 'list'.
616 while ((dir = FcStrListNext (list)))
619 file = (FcChar8 *) malloc (strlen ((char *) dir) + 1 + FC_MAX_FILE_LEN + 1);
623 strcpy ((char *) file, (char *) dir);
624 strcat ((char *) file, "/");
625 base = file + strlen ((char *) file);
627 subdirs = FcStrSetCreate ();
630 fprintf (stderr, "Can't create directory set\n");
636 if (access ((char *) dir, X_OK) < 0)
644 fprintf (stderr, "\"%s\": ", dir);
648 FcStrSetDestroy (subdirs);
652 if (stat ((char *) dir, &statb) == -1)
654 fprintf (stderr, "\"%s\": ", dir);
656 FcStrSetDestroy (subdirs);
661 if (!S_ISDIR (statb.st_mode))
663 fprintf (stderr, "\"%s\": not a directory, skipping\n", dir);
664 FcStrSetDestroy (subdirs);
668 if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, subdirs, dir))
670 if (FcDebug () & FC_DBG_FONTSET)
671 printf ("cache scan dir %s\n", dir);
673 FcDirScanConfig (set, subdirs, cache,
674 config->blanks, dir, FcFalse, config);
676 sublist = FcStrListCreate (subdirs);
677 FcStrSetDestroy (subdirs);
680 fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir);
685 ret += FcCacheReadDirs (config, cache, sublist, set);
688 FcStrListDone (list);
693 FcCacheRead (FcConfig *config, FcGlobalCache * cache)
695 FcFontSet * s = FcFontSetCreate();
699 if (FcCacheReadDirs (config, cache, FcConfigGetConfigDirs (config), s))
705 FcFontSetDestroy (s);
709 /* read serialized state from the cache file */
711 FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir)
713 char *cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
715 char * current_arch_machine_name;
716 char candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
717 off_t current_arch_start = 0;
718 char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
723 current_arch_machine_name = FcCacheMachineSignature();
724 fd = open(cache_file, O_RDONLY);
728 current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
729 if (current_arch_start < 0)
732 lseek (fd, current_arch_start, SEEK_SET);
733 if (FcCacheReadString (fd, candidate_arch_machine_name,
734 sizeof (candidate_arch_machine_name)) == 0)
737 while (strlen(FcCacheReadString (fd, subdirName, sizeof (subdirName))) > 0)
738 FcStrSetAdd (dirs, (FcChar8 *)subdirName);
740 if (!FcDirCacheConsume (fd, (const char *)dir, set))
755 FcDirCacheConsume (int fd, const char * dir, FcFontSet *set)
758 void * current_dir_block;
761 read(fd, &metadata, sizeof(FcCache));
762 if (metadata.magic != FC_CACHE_MAGIC)
768 pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
769 current_dir_block = mmap (0, metadata.count,
770 PROT_READ, MAP_SHARED, fd, pos);
771 if (current_dir_block == MAP_FAILED)
774 if (!FcFontSetUnserialize (metadata, set, current_dir_block))
777 FcCacheAddBankDir (metadata.bank, dir);
783 FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
785 void * current_dir_block, * final_dir_block;
786 static unsigned int rand_state = 0;
790 rand_state = time(0L);
791 bank = rand_r(&rand_state);
793 while (FcCacheHaveBank(bank))
794 bank = rand_r(&rand_state);
796 memset (metadata, 0, sizeof(FcCache));
798 metadata->count = FcFontSetNeededBytes (set);
799 metadata->magic = FC_CACHE_MAGIC;
800 metadata->bank = bank;
802 if (!metadata->count) /* not a failure, no fonts to write */
805 current_dir_block = malloc (metadata->count);
806 if (!current_dir_block)
808 final_dir_block = FcFontSetDistributeBytes (metadata, current_dir_block);
810 if ((char *)current_dir_block + metadata->count != final_dir_block)
813 if (!FcFontSetSerialize (bank, set))
816 return current_dir_block;
819 free (current_dir_block);
823 /* write serialized state to the cache file */
825 FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
827 FcChar8 *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
828 int fd, fd_orig, i, dirs_count;
831 off_t current_arch_start = 0, truncate_to;
833 char *current_arch_machine_name, * header;
834 void *current_dir_block;
839 current_dir_block = FcDirCacheProduce (set, &metadata);
841 if (metadata.count && !current_dir_block)
844 if (FcDebug () & FC_DBG_CACHE)
845 printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
847 atomic = FcAtomicCreate (cache_file);
851 if (!FcAtomicLock (atomic))
854 fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY, 0666);
856 fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT, 0666);
860 current_arch_machine_name = FcCacheMachineSignature ();
861 current_arch_start = 0;
865 FcCacheSkipToArch(fd_orig, current_arch_machine_name);
867 if (current_arch_start < 0)
868 current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
870 if (fd_orig != -1 && !FcCacheCopyOld(fd, fd_orig, current_arch_start))
876 current_arch_start = lseek(fd, 0, SEEK_CUR);
877 if (ftruncate (fd, current_arch_start) == -1)
880 /* allocate space for subdir names in this block */
882 for (i = 0; i < dirs->size; i++)
883 dirs_count += strlen((char *)dirs->strs[i]) + 1;
886 /* now write the address of the next offset */
887 truncate_to = FcCacheNextOffset (FcCacheNextOffset (current_arch_start + sizeof (FcCache) + dirs_count) + metadata.count) - current_arch_start;
888 header = malloc (10 + strlen (current_arch_machine_name));
891 sprintf (header, "%8x ", (int)truncate_to);
892 strcat (header, current_arch_machine_name);
893 if (!FcCacheWriteString (fd, header))
896 for (i = 0; i < dirs->size; i++)
897 FcCacheWriteString (fd, (char *)dirs->strs[i]);
898 FcCacheWriteString (fd, "");
900 write (fd, &metadata, sizeof(FcCache));
903 lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_END)), SEEK_SET);
904 write (fd, current_dir_block, metadata.count);
905 free (current_dir_block);
908 /* this actually serves to pad out the cache file, if needed */
909 if (ftruncate (fd, current_arch_start + truncate_to) == -1)
913 if (!FcAtomicReplaceOrig(atomic))
915 FcAtomicUnlock (atomic);
916 FcAtomicDestroy (atomic);
924 FcAtomicUnlock (atomic);
926 FcAtomicDestroy (atomic);
928 unlink ((char *)cache_file);
930 if (current_dir_block)
931 free (current_dir_block);
937 FcCacheMachineSignature ()
939 static char buf[MACHINE_SIGNATURE_SIZE];
940 int magic = ENDIAN_TEST;
941 char * m = (char *)&magic;
943 sprintf (buf, "%2x%2x%2x%2x "
944 "%4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x "
945 "%4x %4x %4x %4x %4x %4x %4x\n",
946 m[0], m[1], m[2], m[3],
947 (unsigned int)sizeof (char),
948 (unsigned int)sizeof (char *),
949 (unsigned int)sizeof (int),
950 (unsigned int)sizeof (FcPattern),
951 (unsigned int)sizeof (FcPatternEltPtr),
952 (unsigned int)sizeof (struct _FcPatternElt *),
953 (unsigned int)sizeof (FcPatternElt),
954 (unsigned int)sizeof (FcObjectPtr),
955 (unsigned int)sizeof (FcValueListPtr),
956 (unsigned int)sizeof (FcValue),
957 (unsigned int)sizeof (FcValueBinding),
958 (unsigned int)sizeof (struct _FcValueList *),
959 (unsigned int)sizeof (FcCharSet),
960 (unsigned int)sizeof (FcCharLeaf **),
961 (unsigned int)sizeof (FcChar16 *),
962 (unsigned int)sizeof (FcChar16),
963 (unsigned int)sizeof (FcCharLeaf),
964 (unsigned int)sizeof (FcChar32),
965 (unsigned int)sizeof (FcCache));
970 static int banks_ptr = 0, banks_alloc = 0;
971 static int * bankId = 0, * bankIdx = 0;
972 static const char ** bankDirs = 0;
975 FcCacheHaveBank (int bank)
979 if (bank < FC_BANK_FIRST)
982 for (i = 0; i < banks_ptr; i++)
983 if (bankId[i] == bank)
990 FcCacheBankToIndex (int bank)
994 for (i = 0; i < banks_ptr; i++)
995 if (bankId[bankIdx[i]] == bank)
999 for (j = i; j > 0; j--)
1000 bankIdx[j] = bankIdx[j-1];
1005 if (banks_ptr >= banks_alloc)
1010 b = realloc (bankId, (banks_alloc + 4) * sizeof(int));
1015 bidx = realloc (bankIdx, (banks_alloc + 4) * sizeof(int));
1020 bds = realloc (bankDirs, (banks_alloc + 4) * sizeof (char *));
1035 FcCacheAddBankDir (int bank, const char * dir)
1037 int bi = FcCacheBankToIndex (bank);
1042 bankDirs[bi] = (const char *)FcStrCopy ((FcChar8 *)dir);
1046 FcCacheFindBankDir (int bank)
1048 int bi = FcCacheBankToIndex (bank);
1049 return bankDirs[bi];