#endif
static int
-FcDirCacheOpen (const FcChar8 * dir);
-
-static char *
-FcDirCacheHashName (char * cache_file, int collisions);
+FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path);
static off_t
FcCacheSkipToArch (int fd, const char * arch);
FcGlobalCacheDirDestroy (d);
}
FcMemFree (FC_MEM_CACHE, sizeof (FcGlobalCache));
+ if (cache->fd != -1)
+ close (cache->fd);
free (cache);
}
}
/* Does not check that the cache has the appropriate arch section. */
-/* Also, this can be fooled if the original location has a stale
- * cache, and the hashed location has an up-to-date cache. Oh well,
- * sucks to be you in that case! */
FcBool
-FcDirCacheValid (const FcChar8 *dir)
+FcDirCacheValid (const FcChar8 *dir, FcConfig *config)
{
- struct stat file_stat, dir_stat;
int fd;
- if (stat ((char *) dir, &dir_stat) < 0)
- return FcFalse;
-
- fd = FcDirCacheOpen (dir);
+ fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
return FcFalse;
- if (fstat (fd, &file_stat) < 0)
- goto bail;
-
close (fd);
- /*
- * If the directory has been modified more recently than
- * the cache file, the cache is not valid
- */
- if (dir_stat.st_mtime > file_stat.st_mtime)
- return FcFalse;
-
return FcTrue;
-
- bail:
- close (fd);
- return FcFalse;
}
/* Assumes that the cache file in 'dir' exists.
* Checks that the cache has the appropriate arch section. */
FcBool
-FcDirCacheHasCurrentArch (const FcChar8 *dir)
+FcDirCacheHasCurrentArch (const FcChar8 *dir, FcConfig *config)
{
int fd;
off_t current_arch_start;
FcCache metadata;
char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
- fd = FcDirCacheOpen (dir);
+ fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
goto bail;
return FcFalse;
}
-FcBool
-FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+#define CACHEBASE_LEN (1 + 32 + sizeof (FC_CACHE_SUFFIX))
+
+static const char bin2hex[] = { '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f' };
+
+static FcChar8 *
+FcDirCacheBasename (const FcChar8 * dir, FcChar8 cache_base[CACHEBASE_LEN])
{
- char *cache_file;
- char *cache_hashed = 0;
- int fd, collisions;
- struct stat cache_stat;
- char name_buf[FC_MAX_FILE_LEN];
+ unsigned char hash[16];
+ FcChar8 *hex_hash;
+ int cnt;
+ struct MD5Context ctx;
- dir = FcConfigNormalizeFontDir (config, dir);
- cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
- if (!cache_file)
- return FcFalse;
+ MD5Init (&ctx);
+ MD5Update (&ctx, (unsigned char *)dir, strlen ((char *) dir));
- /* First remove normal cache file. */
- if (stat ((char *) cache_file, &cache_stat) == 0)
- unlink ((char *)cache_file);
+ MD5Final (hash, &ctx);
- /* Next remove any applicable hashed files. */
- fd = -1; collisions = 0;
- do
+ cache_base[0] = '/';
+ hex_hash = cache_base + 1;
+ for (cnt = 0; cnt < 16; ++cnt)
{
- if (cache_hashed)
- FcStrFree ((FcChar8 *)cache_hashed);
+ hex_hash[2*cnt ] = bin2hex[hash[cnt] >> 4];
+ hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
+ }
+ hex_hash[2*cnt] = 0;
+ strcat ((char *) cache_base, FC_CACHE_SUFFIX);
- cache_hashed = FcDirCacheHashName (cache_file, collisions++);
- if (!cache_hashed)
- goto bail;
+ return cache_base;
+}
- if (fd > 0)
- close (fd);
- fd = open(cache_hashed, O_RDONLY | O_BINARY);
- if (fd == -1)
- {
- FcStrFree ((FcChar8 *)cache_hashed);
- FcStrFree ((FcChar8 *)cache_file);
- return FcTrue;
- }
+FcBool
+FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
+{
+ int fd = -1;
+ FcChar8 *cache_hashed = NULL;
+ FcChar8 cache_base[CACHEBASE_LEN];
+ FcStrList *list;
+ FcChar8 *cache_dir;
+ char dir_buf[FC_MAX_FILE_LEN];
- if (!FcCacheReadString (fd, name_buf, sizeof (name_buf)) || !strlen(name_buf))
- {
- FcStrFree ((FcChar8 *)cache_hashed);
- goto bail;
- }
- } while (strcmp (name_buf, cache_file) != 0);
+ dir = FcConfigNormalizeFontDir (config, dir);
- close (fd);
+ FcDirCacheBasename (dir, cache_base);
- if (stat ((char *) cache_hashed, &cache_stat) == 0 &&
- unlink ((char *)cache_hashed) != 0)
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+ return FcFalse;
+
+ while ((cache_dir = FcStrListNext (list)))
{
- FcStrFree ((FcChar8 *)cache_hashed);
- goto bail;
+ cache_hashed = FcStrPlus (cache_dir, cache_base);
+ if (!cache_hashed)
+ break;
+ fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
+ FcStrFree (cache_hashed);
+ if (fd >= 0)
+ {
+ if (FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) &&
+ strcmp (dir_buf, (char *) dir) == 0)
+ {
+ close (fd);
+ if (unlink ((char *) cache_hashed) < 0)
+ break;
+ } else
+ close (fd);
+ }
}
-
- FcStrFree ((FcChar8 *)cache_file);
- FcStrFree ((FcChar8 *)cache_hashed);
+ FcStrListDone (list);
+ /* return FcFalse if something went wrong */
+ if (cache_dir)
+ return FcFalse;
return FcTrue;
-
- bail:
- FcStrFree ((FcChar8 *)cache_file);
- return FcFalse;
}
static int
FcStrSetDestroy (subdirs);
continue;
}
- if (FcDirCacheValid (dir) &&
- FcDirCacheHasCurrentArch (dir) &&
- FcDirCacheRead (set, subdirs, dir, config))
+ if (FcDirCacheRead (set, subdirs, dir, config))
{
/* if an old entry is found in the global cache, disable it */
if ((d = FcGlobalCacheDirFind (cache, (const char *)dir)) != NULL)
return 0;
}
-static const char bin2hex[] = { '0', '1', '2', '3',
- '4', '5', '6', '7',
- '8', '9', 'a', 'b',
- 'c', 'd', 'e', 'f' };
-
-static char *
-FcDirCacheHashName (char * cache_file, int collisions)
-{
- unsigned char hash[16], hex_hash[33];
- char *cache_hashed;
- unsigned char uscore = '_';
- int cnt, i;
- FcChar8 *tmp;
- struct MD5Context ctx;
-
- MD5Init (&ctx);
- MD5Update (&ctx, (unsigned char *)cache_file, strlen (cache_file));
-
- for (i = 0; i < collisions; i++)
- MD5Update (&ctx, &uscore, 1);
-
- MD5Final (hash, &ctx);
-
- for (cnt = 0; cnt < 16; ++cnt)
- {
- hex_hash[2*cnt] = bin2hex[hash[cnt] >> 4];
- hex_hash[2*cnt+1] = bin2hex[hash[cnt] & 0xf];
- }
- hex_hash[32] = 0;
-
- tmp = FcStrPlus ((FcChar8 *)hex_hash, (FcChar8 *)FC_CACHE_SUFFIX);
- if (!tmp)
- return 0;
-
- cache_hashed = (char *)FcStrPlus ((FcChar8 *)PKGCACHEDIR"/", tmp);
- free (tmp);
-
- return cache_hashed;
-}
-
-/* Opens the hashed name for cache_file.
- * This would fail in the unlikely event of a collision and subsequent
- * removal of the file which originally caused the collision. */
+/* Opens the cache file for 'dir' for reading.
+ * This searches the list of cache dirs for the relevant cache file,
+ * returning the first one found.
+ */
static int
-FcDirCacheOpen (const FcChar8 *dir)
+FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path)
{
- FcBool found;
- int fd = -1, collisions = 0;
- char *cache_file, *cache_hashed;
- char name_buf[FC_MAX_FILE_LEN];
- struct stat dir_stat;
+ int fd = -1;
+ FcChar8 *cache_hashed = NULL;
+ FcChar8 cache_base[CACHEBASE_LEN];
+ FcStrList *list;
+ FcChar8 *cache_dir;
+ char dir_buf[FC_MAX_FILE_LEN];
+ struct stat file_stat, dir_stat;
- if (stat ((char *)dir, &dir_stat) == -1)
- return -1;
+ if (stat ((char *) dir, &dir_stat) < 0)
+ return -1;
- cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
- if (!cache_file)
- return -1;
+ FcDirCacheBasename (dir, cache_base);
- found = FcFalse;
- do
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+ return -1;
+
+ while ((cache_dir = FcStrListNext (list)))
{
- struct stat c;
- FcChar8 *name_buf_dir;
-
- if (fd >= 0)
- close (fd);
-
- cache_hashed = FcDirCacheHashName (cache_file, collisions++);
- if (!cache_hashed)
- {
- FcStrFree ((FcChar8 *)cache_file);
- return -1;
- }
-
- fd = open(cache_hashed, O_RDONLY | O_BINARY);
- FcStrFree ((FcChar8 *)cache_hashed);
-
- if (fd == -1)
+ cache_hashed = FcStrPlus (cache_dir, cache_base);
+ if (!cache_hashed)
break;
- if (!FcCacheReadString (fd, name_buf, sizeof (name_buf)) ||
- !strlen(name_buf))
- break;
-
- name_buf_dir = FcStrDirname ((FcChar8 *)name_buf);
- if (stat ((char *)name_buf_dir, &c) == -1)
- {
- FcStrFree (name_buf_dir);
- continue;
+ fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
+ if (fd >= 0) {
+ if (fstat (fd, &file_stat) >= 0 &&
+ dir_stat.st_mtime <= file_stat.st_mtime)
+ {
+ break;
+ }
+ close (fd);
+ fd = -1;
}
- FcStrFree (name_buf_dir);
- found = (c.st_ino == dir_stat.st_ino) && (c.st_dev == dir_stat.st_dev);
- } while (!found);
+ FcStrFree (cache_hashed);
+ cache_hashed = NULL;
+ }
+ FcStrListDone (list);
- if (!found || fd < 0)
+ if (fd < 0)
+ return -1;
+
+ if (!FcCacheReadString (fd, dir_buf, sizeof (dir_buf)) ||
+ strcmp (dir_buf, (char *) dir) != 0)
{
- if (fd >= 0)
- close (fd);
- fd = open(cache_file, O_RDONLY | O_BINARY);
+ close (fd);
+ FcStrFree (cache_hashed);
+ return -1;
}
+
+ if (cache_path)
+ *cache_path = cache_hashed;
+ else
+ FcStrFree (cache_hashed);
- FcStrFree ((FcChar8 *)cache_file);
return fd;
}
off_t current_arch_start = 0;
char subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
- fd = FcDirCacheOpen (dir);
+ fd = FcDirCacheOpen (config, dir, NULL);
if (fd < 0)
goto bail;
return 0;
}
+static FcBool
+FcMakeDirectory (const FcChar8 *dir)
+{
+ FcChar8 *parent;
+ FcBool ret;
+
+ if (strlen ((char *) dir) == 0)
+ return FcFalse;
+
+ parent = FcStrDirname (dir);
+ if (!parent)
+ return FcFalse;
+ if (access ((char *) parent, W_OK|X_OK) == 0)
+ ret = mkdir ((char *) dir, 0777) == 0;
+ else if (access ((char *) parent, F_OK) == -1)
+ ret = FcMakeDirectory (parent) && (mkdir ((char *) dir, 0777) == 0);
+ else
+ ret = FcFalse;
+ FcStrFree (parent);
+ return ret;
+}
+
/* write serialized state to the cache file */
FcBool
-FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
+FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config)
{
- char *cache_file;
- char *cache_hashed;
+ FcChar8 cache_base[CACHEBASE_LEN];
+ FcChar8 *cache_hashed;
int fd, fd_orig, i, dirs_count;
FcAtomic *atomic;
FcCache metadata;
off_t current_arch_start = 0, truncate_to;
- char name_buf[FC_MAX_FILE_LEN];
- int collisions;
-
+ FcStrList *list;
char *current_arch_machine_name, * header;
void *current_dir_block = 0;
+ FcChar8 *cache_dir = NULL;
+ FcChar8 *test_dir;
dir = FcConfigNormalizeFontDir (FcConfigGetCurrent(), dir);
if (!dir)
return FcFalse;
- cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
- if (!cache_file)
- goto bail;
-
- /* Ensure that we're not trampling a cache for some other dir. */
- /* This is slightly different from FcDirCacheOpen, since it
- * needs the filename, not the file descriptor. */
- fd = -1; collisions = 0;
- do
- {
- cache_hashed = FcDirCacheHashName (cache_file, collisions++);
- if (!cache_hashed)
- goto bail0;
-
- if (fd > 0)
- close (fd);
- fd = open(cache_hashed, O_RDONLY | O_BINARY);
- if (fd == -1)
+ /*
+ * Write it to the first directory in the list which is writable
+ */
+
+ list = FcStrListCreate (config->cacheDirs);
+ if (!list)
+ return FcFalse;
+ while ((test_dir = FcStrListNext (list))) {
+ if (access ((char *) test_dir, W_OK|X_OK) == 0)
+ {
+ cache_dir = test_dir;
break;
- if(!FcCacheReadString (fd, name_buf, sizeof (name_buf)) || !strlen(name_buf))
+ }
+ else
{
- close (fd);
- FcStrFree ((FcChar8 *)cache_hashed);
- continue;
+ /*
+ * If the directory doesn't exist, try to create it
+ */
+ if (access ((char *) test_dir, F_OK) == -1) {
+ if (FcMakeDirectory (test_dir))
+ {
+ cache_dir = test_dir;
+ break;
+ }
+ }
}
- close (fd);
-
- if (strcmp (name_buf, cache_file) != 0)
- {
- FcStrFree ((FcChar8 *)cache_hashed);
- continue;
- }
-
- break;
- } while (1);
+ }
+ FcStrListDone (list);
+ if (!cache_dir)
+ return FcFalse;
+ FcDirCacheBasename (dir, cache_base);
+ cache_hashed = FcStrPlus (cache_dir, cache_base);
+ if (!cache_hashed)
+ return FcFalse;
current_dir_block = FcDirCacheProduce (set, &metadata);
goto bail1;
if (FcDebug () & FC_DBG_CACHE)
- printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
+ printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n",
+ dir, cache_hashed);
atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
if (!atomic)
goto bail1;
if (!FcAtomicLock (atomic))
- {
- /* Now try rewriting the original version of the file. */
- FcAtomicDestroy (atomic);
-
- atomic = FcAtomicCreate ((FcChar8 *)cache_file);
- fd_orig = open (cache_file, O_RDONLY | O_BINARY);
- if (fd_orig == -1)
- fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY | O_BINARY);
+ goto bail2;
- fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
- if (fd == -1)
- goto bail2;
- }
-
- /* In all cases, try opening the real location of the cache file first. */
- /* (even if that's not atomic.) */
- fd_orig = open (cache_file, O_RDONLY | O_BINARY);
- if (fd_orig == -1)
- fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY | O_BINARY);
+ /* open the original file to save relevant portions */
+ fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY | O_BINARY);
fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
if (fd == -1)
goto bail3;
- FcCacheWriteString (fd, cache_file);
+ FcCacheWriteString (fd, (char *) dir);
current_arch_machine_name = FcCacheMachineSignature ();
current_arch_start = 0;
if (!FcAtomicReplaceOrig(atomic))
goto bail3;
FcStrFree ((FcChar8 *)cache_hashed);
- FcStrFree ((FcChar8 *)cache_file);
FcAtomicUnlock (atomic);
FcAtomicDestroy (atomic);
return FcTrue;
FcAtomicDestroy (atomic);
bail1:
FcStrFree ((FcChar8 *)cache_hashed);
- bail0:
- unlink ((char *)cache_file);
- FcStrFree ((FcChar8 *)cache_file);
if (current_dir_block)
free (current_dir_block);
- bail:
return FcFalse;
}