]> git.wh0rd.org - fontconfig.git/blobdiff - src/fccache.c
Stop trampling the directory name when writing out caches. (with Mike
[fontconfig.git] / src / fccache.c
index 311fb7d8903022de7dbd5bf0316369294139a9d4..349bc9ab500a69a12e32d5b107af1afbca873417 100644 (file)
 #define MACHINE_SIGNATURE_SIZE 9 + 5*20 + 1
 
 static int
-FcDirCacheOpen (char * cache_file);
+FcDirCacheOpen (const FcChar8 * dir);
 
 static char *
 FcDirCacheHashName (char * cache_file, int collisions);
 
 static off_t
-FcCacheSkipToArch (int fd, const char * arch, FcBool global);
+FcCacheSkipToArch (int fd, const char * arch);
 
 static FcBool 
 FcCacheCopyOld (int fd, int fd_orig, off_t start);
@@ -52,10 +52,7 @@ static void *
 FcDirCacheProduce (FcFontSet *set, FcCache * metadata);
 
 static FcBool
-FcDirCacheConsume (int fd, const char * dir, FcFontSet *set);
-
-FcBool
-FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir);
+FcDirCacheConsume (int fd, const char * dir, FcFontSet *set, FcConfig *config);
 
 static int
 FcCacheNextOffset(off_t w);
@@ -219,9 +216,13 @@ FcGlobalCacheLoad (FcGlobalCache    *cache,
 
     cache->updated = FcFalse;
 
+    FcCacheReadString (cache->fd, name_buf, sizeof (name_buf));
+    if (strcmp (name_buf, FC_GLOBAL_MAGIC_COOKIE) != 0)
+       return;
+
     current_arch_machine_name = FcCacheMachineSignature ();
     current_arch_start = FcCacheSkipToArch(cache->fd, 
-                                          current_arch_machine_name, FcTrue);
+                                          current_arch_machine_name);
     if (current_arch_start < 0)
         goto bail_and_destroy;
 
@@ -303,12 +304,13 @@ FcGlobalCacheReadDir (FcFontSet *set, FcStrSet *dirs, FcGlobalCache * cache, con
     if (cache->fd == -1)
        return FcFalse;
 
+    dir = (char *)FcConfigNormalizeFontDir (config, (FcChar8 *)dir);
     for (d = cache->dirs; d; d = d->next)
     {
        if (strncmp (d->name, dir, strlen(dir)) == 0)
        {
            lseek (cache->fd, d->offset, SEEK_SET);
-           if (!FcDirCacheConsume (cache->fd, dir, set))
+           if (!FcDirCacheConsume (cache->fd, dir, set, config))
                return FcFalse;
             if (strcmp (d->name, dir) == 0)
                ret = FcTrue;
@@ -321,13 +323,12 @@ FcGlobalCacheReadDir (FcFontSet *set, FcStrSet *dirs, FcGlobalCache * cache, con
 FcBool
 FcGlobalCacheUpdate (FcGlobalCache  *cache,
                     const char     *name,
-                    FcFontSet      *set)
+                    FcFontSet      *set,
+                    FcConfig       *config)
 {
     FcGlobalCacheDir * d;
 
-    if (!set->nfont)
-       return FcTrue;
-
+    name = (char *)FcConfigNormalizeFontDir (config, (FcChar8 *)name);
     for (d = cache->dirs; d; d = d->next)
     {
        if (strcmp(d->name, name) == 0)
@@ -353,7 +354,8 @@ FcGlobalCacheUpdate (FcGlobalCache  *cache,
 
 FcBool
 FcGlobalCacheSave (FcGlobalCache    *cache,
-                  const FcChar8    *cache_file)
+                  const FcChar8    *cache_file,
+                  FcConfig         *config)
 {
     int                        fd, fd_orig;
     FcGlobalCacheDir   *dir;
@@ -380,6 +382,7 @@ FcGlobalCacheSave (FcGlobalCache    *cache,
               S_IRUSR | S_IWUSR);
     if (fd == -1)
        goto bail2;
+    FcCacheWriteString (fd, FC_GLOBAL_MAGIC_COOKIE);
 
     fd_orig = open ((char *) FcAtomicOrigFile(atomic), O_RDONLY);
 
@@ -388,11 +391,15 @@ FcGlobalCacheSave (FcGlobalCache    *cache,
         current_arch_start = 0;
     else
         current_arch_start = FcCacheSkipToArch (fd_orig, 
-                                                current_arch_machine_name, 
-                                               FcTrue);
+                                                current_arch_machine_name);
 
     if (current_arch_start < 0)
-       current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
+    {
+       off_t i = lseek(fd_orig, 0, SEEK_END);
+       if (i < strlen (FC_GLOBAL_MAGIC_COOKIE)+1)
+           i = strlen (FC_GLOBAL_MAGIC_COOKIE)+1;
+       current_arch_start = FcCacheNextOffset (i);
+    }
 
     if (!FcCacheCopyOld(fd, fd_orig, current_arch_start))
        goto bail3;
@@ -425,9 +432,11 @@ FcGlobalCacheSave (FcGlobalCache    *cache,
 
     for (dir = cache->dirs; dir; dir = dir->next)
     {
-        if (dir->ent)
+        if (dir->name)
         {
-            FcCacheWriteString (fd, dir->name);
+           const char * d = (const char *)FcConfigNormalizeFontDir (config, (const FcChar8 *)dir->name);
+
+            FcCacheWriteString (fd, d);
             write (fd, &dir->metadata, sizeof(FcCache));
             lseek (fd, FcCacheNextOffset (lseek(fd, 0, SEEK_CUR)), SEEK_SET);
             write (fd, dir->ent, dir->metadata.count);
@@ -483,15 +492,14 @@ FcCacheNextOffset(off_t w)
 /* return the address of the segment for the provided arch,
  * or -1 if arch not found */
 static off_t
-FcCacheSkipToArch (int fd, const char * arch, FcBool global)
+FcCacheSkipToArch (int fd, const char * arch)
 {
     char candidate_arch_machine_name_count[MACHINE_SIGNATURE_SIZE + 9];
     char * candidate_arch;
     off_t current_arch_start = 0;
 
     lseek (fd, 0, SEEK_SET);
-    if (!global)
-       FcCacheSkipString (fd);
+    FcCacheSkipString (fd);
     current_arch_start = lseek (fd, 0, SEEK_CUR);
 
     /* skip arches that are not the current arch */
@@ -596,26 +604,20 @@ FcCacheCopyOld (int fd, int fd_orig, off_t start)
 FcBool
 FcDirCacheValid (const FcChar8 *dir)
 {
-    FcChar8    *cache_file;
     struct stat file_stat, dir_stat;
     int        fd;
 
     if (stat ((char *) dir, &dir_stat) < 0)
         return FcFalse;
 
-    cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
-    if (!cache_file)
-       return FcFalse;
-
-    fd = FcDirCacheOpen ((char *)cache_file);
+    fd = FcDirCacheOpen (dir);
 
     if (fd < 0)
        goto bail;
     if (fstat (fd, &file_stat) < 0)
-       goto bail1;
+       goto bail;
 
     close (fd);
-    FcStrFree (cache_file);
 
     /*
      * If the directory has been modified more recently than
@@ -626,10 +628,8 @@ FcDirCacheValid (const FcChar8 *dir)
 
     return FcTrue;
 
- bail1:
-    close (fd);
  bail:
-    FcStrFree (cache_file);
+    close (fd);
     return FcFalse;
 }
 
@@ -638,22 +638,16 @@ FcDirCacheValid (const FcChar8 *dir)
 FcBool
 FcDirCacheHasCurrentArch (const FcChar8 *dir)
 {
-    char       *cache_file;
     int        fd;
     off_t      current_arch_start;
     char       *current_arch_machine_name;
 
-    cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
-    if (!cache_file)
-       return FcFalse;
-
-    fd = FcDirCacheOpen (cache_file);
+    fd = FcDirCacheOpen (dir);
     if (fd < 0)
        goto bail;
 
     current_arch_machine_name = FcCacheMachineSignature();
-    current_arch_start = FcCacheSkipToArch(fd, 
-                                          current_arch_machine_name, FcFalse);
+    current_arch_start = FcCacheSkipToArch(fd, current_arch_machine_name);
     close (fd);
 
     if (current_arch_start < 0)
@@ -662,25 +656,72 @@ FcDirCacheHasCurrentArch (const FcChar8 *dir)
     return FcTrue;
 
  bail:
-    free (cache_file);
     return FcFalse;
 }
 
 FcBool
-FcDirCacheUnlink (const FcChar8 *dir)
+FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config)
 {
-    FcChar8     *cache_file = FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
+    char       *cache_file;
+    char       *cache_hashed = 0;
+    int                fd, collisions;
     struct stat        cache_stat;
+    char       name_buf[FC_MAX_FILE_LEN];
+
+    dir = FcConfigNormalizeFontDir (config, dir);
+    cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
+    if (!cache_file)
+       return FcFalse;
 
+    /* First remove normal cache file. */
     if (stat ((char *) cache_file, &cache_stat) == 0 &&
        unlink ((char *)cache_file) != 0)
+       goto bail;
+
+    /* Next remove any applicable hashed files. */
+    fd = -1; collisions = 0;
+    do
     {
-       FcStrFree (cache_file);
-        return FcFalse;
+       if (cache_hashed)
+           FcStrFree ((FcChar8 *)cache_hashed);
+
+       cache_hashed = FcDirCacheHashName (cache_file, collisions++);
+       if (!cache_hashed)
+           goto bail;
+
+       if (fd > 0)
+           close (fd);
+       fd = open(cache_hashed, O_RDONLY);
+       if (fd == -1)
+       {
+           FcStrFree ((FcChar8 *)cache_file);
+           return FcTrue;
+       }
+
+       FcCacheReadString (fd, name_buf, sizeof (name_buf));
+       if (!strlen(name_buf))
+       {
+           FcStrFree ((FcChar8 *)cache_hashed);
+           goto bail;
+       }
+    } while (strcmp (name_buf, cache_file) != 0);
+
+    FcStrFree ((FcChar8 *)cache_file);
+    close (fd);
+
+    if (stat ((char *) cache_hashed, &cache_stat) == 0 &&
+       unlink ((char *)cache_hashed) != 0)
+    {
+       FcStrFree ((FcChar8 *)cache_hashed);
+       goto bail;
     }
 
-    FcStrFree (cache_file);
+    FcStrFree ((FcChar8 *)cache_hashed);
     return FcTrue;
+
+ bail:
+    FcStrFree ((FcChar8 *)cache_file);
+    return FcFalse;
 }
 
 static int
@@ -752,7 +793,7 @@ FcCacheReadDirs (FcConfig * config, FcGlobalCache * cache,
            free (file);
            continue;
        }
-       if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, subdirs, dir))
+       if (!FcDirCacheValid (dir) || !FcDirCacheRead (set, subdirs, dir, config))
        {
            if (FcDebug () & FC_DBG_FONTSET)
                printf ("cache scan dir %s\n", dir);
@@ -837,61 +878,87 @@ FcDirCacheHashName (char * cache_file, int collisions)
  * This would fail in the unlikely event of a collision and subsequent
  * removal of the file which originally caused the collision. */
 static int
-FcDirCacheOpen (char *cache_file)
+FcDirCacheOpen (const FcChar8 *dir)
 {
+    FcBool     found;
     int                fd = -1, collisions = 0;
-    char       *cache_hashed;
+    char       *cache_file, *cache_hashed;
     char       name_buf[FC_MAX_FILE_LEN];
+    struct stat dir_stat;
+
+    cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
+    if (!cache_file)
+       return -1;
 
     fd = open(cache_file, O_RDONLY);
     if (fd != -1)
        return fd;
 
+    if (stat ((char *)dir, &dir_stat) == -1)
+       return -1;
+
+    found = FcFalse;
     do
     {
+       struct stat c;
+       FcChar8 * name_buf_dir;
+
        cache_hashed = FcDirCacheHashName (cache_file, collisions++);
        if (!cache_hashed)
+       {
+           FcStrFree ((FcChar8 *)cache_file);
            return -1;
+       }
 
        if (fd > 0)
            close (fd);
        fd = open(cache_hashed, O_RDONLY);
+       FcStrFree ((FcChar8 *)cache_hashed);
+
        if (fd == -1)
+       {
+           FcStrFree ((FcChar8 *)cache_file);
            return -1;
+       }
        FcCacheReadString (fd, name_buf, sizeof (name_buf));
        if (!strlen(name_buf))
            goto bail;
-    } while (strcmp (name_buf, cache_file) != 0);
+
+       name_buf_dir = FcStrDirname ((FcChar8 *)name_buf);
+       if (stat ((char *)name_buf_dir, &c) == -1)
+       {
+           FcStrFree (name_buf_dir);
+           continue;
+       }
+       FcStrFree (name_buf_dir);
+       found = (c.st_ino == dir_stat.st_ino) && (c.st_dev == dir_stat.st_dev);
+    } while (!found);
+    FcStrFree ((FcChar8 *)cache_file);
     return fd;
 
  bail:
+    FcStrFree ((FcChar8 *)cache_file);
     close (fd);
     return -1;
 }
 
 /* read serialized state from the cache file */
 FcBool
-FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir)
+FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir, FcConfig *config)
 {
-    char       *cache_file;
     int        fd;
     char       *current_arch_machine_name;
     char       candidate_arch_machine_name[9+MACHINE_SIGNATURE_SIZE];
     off_t      current_arch_start = 0;
     char       subdirName[FC_MAX_FILE_LEN + 1 + 12 + 1];
 
-    cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
-    if (!cache_file)
-       return FcFalse;
-
-    fd = FcDirCacheOpen (cache_file);
+    fd = FcDirCacheOpen (dir);
     if (fd < 0)
        goto bail;
 
     current_arch_machine_name = FcCacheMachineSignature();
     current_arch_start = FcCacheSkipToArch(fd, 
-                                          current_arch_machine_name,
-                                          FcFalse);
+                                          current_arch_machine_name);
     if (current_arch_start < 0)
         goto bail1;
 
@@ -903,22 +970,20 @@ FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, const FcChar8 *dir)
     while (strlen(FcCacheReadString (fd, subdirName, sizeof (subdirName))) > 0)
         FcStrSetAdd (dirs, (FcChar8 *)subdirName);
 
-    if (!FcDirCacheConsume (fd, (const char *)dir, set))
+    if (!FcDirCacheConsume (fd, (const char *)dir, set, config))
        goto bail1;
        
     close(fd);
-    free (cache_file);
     return FcTrue;
 
  bail1:
     close (fd);
  bail:
-    free (cache_file);
     return FcFalse;
 }
 
 static FcBool
-FcDirCacheConsume (int fd, const char * dir, FcFontSet *set)
+FcDirCacheConsume (int fd, const char * dir, FcFontSet *set, FcConfig *config)
 {
     FcCache metadata;
     void * current_dir_block;
@@ -929,15 +994,24 @@ FcDirCacheConsume (int fd, const char * dir, FcFontSet *set)
         return FcFalse;
 
     if (!metadata.count)
+    {
+       pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
+       lseek (fd, pos, SEEK_SET);
+       if (config)
+           FcConfigAddFontDir (config, (FcChar8 *)dir);
        return FcTrue;
+    }
 
     pos = FcCacheNextOffset (lseek(fd, 0, SEEK_CUR));
     current_dir_block = mmap (0, metadata.count, 
                              PROT_READ, MAP_SHARED, fd, pos);
+    lseek (fd, pos+metadata.count, SEEK_SET);
     if (current_dir_block == MAP_FAILED)
        return FcFalse;
 
     FcCacheAddBankDir (metadata.bank, dir);
+    if (config)
+       FcConfigAddFontDir (config, (FcChar8 *)dir);
 
     if (!FcFontSetUnserialize (&metadata, set, current_dir_block))
        return FcFalse;
@@ -999,7 +1073,7 @@ FcBool
 FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
 {
     char           *cache_file;
-    char           *cache_to_open;
+    char           *cache_hashed;
     int            fd, fd_orig, i, dirs_count;
     FcAtomic       *atomic;
     FcCache        metadata;
@@ -1010,6 +1084,10 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
     char            *current_arch_machine_name, * header;
     void           *current_dir_block = 0;
 
+    dir = FcConfigNormalizeFontDir (FcConfigGetCurrent(), dir);
+    if (!dir)
+       return FcFalse;
+
     cache_file = (char *)FcStrPlus (dir, (FcChar8 *) "/" FC_DIR_CACHE_FILE);
     if (!cache_file)
         goto bail;
@@ -1020,13 +1098,13 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
     fd = -1; collisions = 0;
     do
     {
-       cache_to_open = FcDirCacheHashName (cache_file, collisions++);
-       if (!cache_to_open)
+       cache_hashed = FcDirCacheHashName (cache_file, collisions++);
+       if (!cache_hashed)
            goto bail0;
 
        if (fd > 0)
            close (fd);
-       fd = open(cache_to_open, O_RDONLY);
+       fd = open(cache_hashed, O_RDONLY);
        if (fd == -1)
            break;
        FcCacheReadString (fd, name_buf, sizeof (name_buf));
@@ -1047,7 +1125,7 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
     if (FcDebug () & FC_DBG_CACHE)
         printf ("FcDirCacheWriteDir cache_file \"%s\"\n", cache_file);
 
-    atomic = FcAtomicCreate ((FcChar8 *)cache_to_open);
+    atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
     if (!atomic)
        goto bail1;
 
@@ -1056,7 +1134,7 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
        /* Now try rewriting the original version of the file. */
        FcAtomicDestroy (atomic);
 
-       atomic = FcAtomicCreate (cache_file);
+       atomic = FcAtomicCreate ((FcChar8 *)cache_file);
        fd_orig = open (cache_file, O_RDONLY);
        if (fd_orig == -1)
            fd_orig = open((char *)FcAtomicOrigFile (atomic), O_RDONLY);
@@ -1083,10 +1161,15 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
 
     if (fd_orig != -1)
         current_arch_start = 
-            FcCacheSkipToArch(fd_orig, current_arch_machine_name, FcFalse);
+            FcCacheSkipToArch(fd_orig, current_arch_machine_name);
 
     if (current_arch_start < 0)
-       current_arch_start = FcCacheNextOffset (lseek(fd_orig, 0, SEEK_END));
+    {
+       off_t i = lseek(fd_orig, 0, SEEK_END);
+       if (i < strlen (FC_GLOBAL_MAGIC_COOKIE)+1)
+           i = strlen (FC_GLOBAL_MAGIC_COOKIE)+1;
+       current_arch_start = FcCacheNextOffset (i);
+    }
 
     if (fd_orig != -1 && !FcCacheCopyOld(fd, fd_orig, current_arch_start))
        goto bail4;
@@ -1130,9 +1213,12 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
     if (ftruncate (fd, current_arch_start + truncate_to) == -1)
        goto bail5;
 
+    free (header);
     close(fd);
     if (!FcAtomicReplaceOrig(atomic))
         goto bail5;
+    FcStrFree ((FcChar8 *)cache_hashed);
+    FcStrFree ((FcChar8 *)cache_file);
     FcAtomicUnlock (atomic);
     FcAtomicDestroy (atomic);
     return FcTrue;
@@ -1146,10 +1232,10 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir)
  bail2:
     FcAtomicDestroy (atomic);
  bail1:
-    free (cache_to_open);
+    FcStrFree ((FcChar8 *)cache_hashed);
  bail0:
     unlink ((char *)cache_file);
-    free (cache_file);
+    FcStrFree ((FcChar8 *)cache_file);
     if (current_dir_block)
         free (current_dir_block);
  bail: