summaryrefslogtreecommitdiff
path: root/src/fcstat.c
diff options
context:
space:
mode:
authorMikhail Gusarov <dottedmag@dottedmag.net>2012-05-28 14:52:21 +0900
committerAkira TAGOH <akira@tagoh.org>2012-05-28 16:46:04 +0900
commit0ac6c98294d666762960824d39329459b22b48b7 (patch)
tree1e7fef214c2d5339bcd6b5f4489070186d47586f /src/fcstat.c
parentdc2da23e69e6b3f6e6d0436d4777ee2c1d8ff1be (diff)
downloadfontconfig-0ac6c98294d666762960824d39329459b22b48b7.tar.gz
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 <dottedmag@dottedmag.net>
Diffstat (limited to 'src/fcstat.c')
-rw-r--r--src/fcstat.c84
1 files changed, 84 insertions, 0 deletions
diff --git a/src/fcstat.c b/src/fcstat.c
index 013e275..c2d9fe9 100644
--- a/src/fcstat.c
+++ b/src/fcstat.c
@@ -130,6 +130,90 @@ FcStat (const FcChar8 *file, struct stat *statb)
#endif
+/* 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;
+}
+
+int
+FcStatChecksum (const FcChar8 *file, struct stat *statb)
+{
+ if (FcStat (file, statb) == -1)
+ return -1;
+
+ if (FcIsFsMtimeBroken (file))
+ {
+ if (FcDirChecksum (file, &statb->st_mtime) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
static int
FcFStatFs (int fd, FcStatFS *statb)
{