From 39727bf598ef2563d6b8246d484fa7be041cd6c2 Mon Sep 17 00:00:00 2001 From: Mikhail Gusarov Date: Sun, 29 Apr 2012 13:06:02 +0200 Subject: [PATCH 2/2] Fix cache aging for fonts on FAT filesystem under Linux Windows does not update mtime of directory on FAT filesystem when file is added to it or removed from it. Fontconfig uses mtime of directory to check cache file aging and hence fails to detect newly added or recently removed files. This changeset detects FAT filesystem (currently implemented for Linux) and adds generating checksum of directory entries instead of using mtime which guarantees proper cache rebuild. For non-FAT filesystems this patch adds single syscall per directory which is negligeable overhead. This fixes bug https://bugs.freedesktop.org/show_bug.cgi?id=25535 Signed-off-by: Mikhail Gusarov --- src/fccache.c | 14 +++--- src/fcdir.c | 2 +- src/fcint.h | 5 ++- src/fcstat.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 9 deletions(-) diff --git a/src/fccache.c b/src/fccache.c index dfe5b83..76e10cb 100644 --- a/src/fccache.c +++ b/src/fccache.c @@ -229,7 +229,7 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir, struct stat file_stat, dir_stat; FcBool ret = FcFalse; - if (FcStat (dir, &dir_stat) < 0) + if (FcStatChecksum (dir, &dir_stat) < 0) return FcFalse; FcDirCacheBasename (dir, cache_base); @@ -556,14 +556,14 @@ FcCacheTimeValid (FcCache *cache, struct stat *dir_stat) if (!dir_stat) { - if (FcStat (FcCacheDir (cache), &dir_static) < 0) + if (FcStatChecksum (FcCacheDir (cache), &dir_static) < 0) return FcFalse; dir_stat = &dir_static; } if (FcDebug () & FC_DBG_CACHE) - printf ("FcCacheTimeValid dir \"%s\" cache time %d dir time %d\n", - FcCacheDir (cache), cache->mtime, (int) dir_stat->st_mtime); - return cache->mtime == (int) dir_stat->st_mtime; + printf ("FcCacheTimeValid dir \"%s\" cache checksum %d dir checksum %d\n", + FcCacheDir (cache), cache->checksum, (int) dir_stat->st_mtime); + return cache->checksum == (int) dir_stat->st_mtime; } /* @@ -727,7 +727,7 @@ FcDirCacheValidateHelper (int fd, struct stat *fd_stat, struct stat *dir_stat, v ret = FcFalse; else if (fd_stat->st_size != c.size) ret = FcFalse; - else if (c.mtime != (int) dir_stat->st_mtime) + else if (c.checksum != (int) dir_stat->st_mtime) ret = FcFalse; return ret; } @@ -802,7 +802,7 @@ FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcSt cache->magic = FC_CACHE_MAGIC_ALLOC; cache->version = FC_CACHE_CONTENT_VERSION; cache->size = serialize->size; - cache->mtime = (int) dir_stat->st_mtime; + cache->checksum = (int) dir_stat->st_mtime; /* * Serialize directory name diff --git a/src/fcdir.c b/src/fcdir.c index 4399afc..2b476e8 100644 --- a/src/fcdir.c +++ b/src/fcdir.c @@ -245,7 +245,7 @@ FcDirCacheScan (const FcChar8 *dir, FcConfig *config) if (FcDebug () & FC_DBG_FONTSET) printf ("cache scan dir %s\n", dir); - if (FcStat (dir, &dir_stat) < 0) + if (FcStatChecksum (dir, &dir_stat) < 0) goto bail; set = FcFontSetCreate(); diff --git a/src/fcint.h b/src/fcint.h index ff655a4..c93a67b 100644 --- a/src/fcint.h +++ b/src/fcint.h @@ -348,7 +348,7 @@ struct _FcCache { intptr_t dirs; /* offset to subdirs */ int dirs_count; /* number of subdir strings */ intptr_t set; /* offset to font set */ - int mtime; /* low bits of directory mtime */ + int checksum; /* checksum of directory state */ }; #undef FcCacheDir @@ -997,6 +997,9 @@ FcMatrixFree (FcMatrix *mat); FcPrivate int FcStat (const FcChar8 *file, struct stat *statb); +FcPrivate int +FcStatChecksum (const FcChar8 *file, struct stat *statb); + /* fcstr.c */ FcPrivate void FcStrSetSort (FcStrSet * set); diff --git a/src/fcstat.c b/src/fcstat.c index 195878a..2663648 100644 --- a/src/fcstat.c +++ b/src/fcstat.c @@ -26,6 +26,10 @@ #include "fcint.h" #include "fcarch.h" #include +#ifdef __linux__ +# include +# include +#endif #ifdef _WIN32 @@ -114,3 +118,132 @@ FcStat (const FcChar8 *file, struct stat *statb) } #endif + +/* + * Checking for FAT filesystem which broken mtime handling. There is no + * cross-platform filesystem type query call, so resort to OS-specific calls. + */ +#ifdef __linux__ +static FcBool +FcFsMtimeBroken (const FcChar8 *file) +{ + int ret; + struct statfs fs_stat; + + if (FcDebug () & FC_DBG_CACHE) + printf ("FcFsMtimeBroken %s\n", file); + + ret = statfs ((const char *)file, &fs_stat); + if (ret == -1) + return FcFalse; + + if (FcDebug () & FC_DBG_CACHE) + printf ("FcFsMtimeBroken f_type %ld MSDOS_SUPER_MAGIC %d\n", + fs_stat.f_type, MSDOS_SUPER_MAGIC); + + if (fs_stat.f_type == MSDOS_SUPER_MAGIC) + return FcTrue; + + return FcFalse; +} +#endif + +/* + * This is to be enabled on all systems which support checking for FAT + * filesystem, not just Linux, so change this #if after updating + * FcFsMtimeBroken. + */ +#ifdef __linux__ + +/* Adler-32 checksum implementation */ +struct Adler32 { + int a; + int b; +}; + +static void +Adler32Init (struct Adler32 *ctx) +{ + ctx->a = 1; + ctx->b = 0; +} + +static void +Adler32Update (struct Adler32 *ctx, const char *data, int data_len) +{ + while (data_len--) + { + ctx->a = (ctx->a + *data++) % 65521; + ctx->b = (ctx->b + ctx->a) % 65521; + } +} + +static int +Adler32Finish (struct Adler32 *ctx) +{ + return ctx->a + (ctx->b << 16); +} + +/* dirent.d_type can be relied upon on FAT filesystem */ +static FcBool +FcDirChecksumScandirFilter(const struct dirent *entry) +{ + return entry->d_type != DT_DIR; +} + +static int +FcDirChecksumScandirSorter(const struct dirent **lhs, const struct dirent **rhs) +{ + return strcmp((*lhs)->d_name, (*rhs)->d_name); +} + +static int +FcDirChecksum (const FcChar8 *dir, time_t *checksum) +{ + struct Adler32 ctx; + struct dirent **files; + int n; + + Adler32Init (&ctx); + + n = scandir ((const char *)dir, &files, + &FcDirChecksumScandirFilter, + &FcDirChecksumScandirSorter); + if (n == -1) + return -1; + + while (n--) + { + Adler32Update (&ctx, files[n]->d_name, strlen(files[n]->d_name) + 1); + Adler32Update (&ctx, (char *)&files[n]->d_type, sizeof(files[n]->d_type)); + free(files[n]); + } + free(files); + + *checksum = Adler32Finish (&ctx); + return 0; +} +#endif + +#ifdef __linux__ +int +FcStatChecksum (const FcChar8 *file, struct stat *statb) +{ + if (FcStat (file, statb) == -1) + return -1; + + if (FcFsMtimeBroken (file)) + { + if (FcDirChecksum (file, &statb->st_mtime) == -1) + return -1; + } + + return 0; +} +#else +int +FcStatChecksum (const FcChar8 *file, struct stat *statb) +{ + return FcStat (flie, statb); +} +#endif -- 1.7.9.5