summaryrefslogtreecommitdiff
path: root/src/fccache.c
diff options
context:
space:
mode:
authorChris Lamb <chris@chris-lamb.co.uk>2018-05-15 22:11:24 +0200
committerAkira TAGOH <akira@tagoh.org>2018-05-16 16:06:46 +0900
commitf098adac54ab86b75a38f2d23fa706a1348f55ba (patch)
tree730a7db937a41060da51953e2125519512c93982 /src/fccache.c
parent0b85e77ede3497b8533b8fcb67d03d8ad174998d (diff)
downloadfontconfig-f098adac54ab86b75a38f2d23fa706a1348f55ba.tar.gz
Ensure cache checksums are deterministic
Whilst working on the Reproducible Builds[0] effort, we noticed that fontconfig generates unreproducible cache files. This is due to fc-cache uses the modification timestamps of each directory in the "checksum" and "checksum_nano" members of the _FcCache struct. This is so that it can identify which cache files are valid and/or require regeneration. This patch changes the behaviour of the checksum calculations to prefer the value of the SOURCE_DATE_EPOCH[1] environment variable over the directory's own mtime. This variable can then be exported by build systems to ensure reproducible output. If SOURCE_DATE_EPOCH is not set or is newer than the mtime of the directory, the existing behaviour is unchanged. This work was sponsored by Tails[2]. [0] https://reproducible-builds.org/ [1] https://reproducible-builds.org/specs/source-date-epoch/ [2] https://tails.boum.org/
Diffstat (limited to 'src/fccache.c')
-rw-r--r--src/fccache.c59
1 files changed, 53 insertions, 6 deletions
diff --git a/src/fccache.c b/src/fccache.c
index 09e876b..27b1282 100644
--- a/src/fccache.c
+++ b/src/fccache.c
@@ -1015,6 +1015,55 @@ FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat)
return cache;
}
+static int
+FcDirChecksum (struct stat *statb)
+{
+ int ret = (int) statb->st_mtime;
+ char *endptr;
+ char *source_date_epoch;
+ unsigned long long epoch;
+
+ source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+ if (source_date_epoch)
+ {
+ epoch = strtoull(source_date_epoch, &endptr, 10);
+
+ if (endptr == source_date_epoch)
+ fprintf (stderr,
+ "Fontconfig: SOURCE_DATE_EPOCH invalid\n");
+ else if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
+ || (errno != 0 && epoch == 0))
+ fprintf (stderr,
+ "Fontconfig: SOURCE_DATE_EPOCH: strtoull: %s: %llu\n",
+ strerror(errno), epoch);
+ else if (*endptr != '\0')
+ fprintf (stderr,
+ "Fontconfig: SOURCE_DATE_EPOCH has trailing garbage\n");
+ else if (epoch > ULONG_MAX)
+ fprintf (stderr,
+ "Fontconfig: SOURCE_DATE_EPOCH must be <= %lu but saw: %llu\n",
+ ULONG_MAX, epoch);
+ else if (epoch < ret)
+ /* Only override if directory is newer */
+ ret = (int) epoch;
+ }
+
+ return ret;
+}
+
+static int64_t
+FcDirChecksumNano (struct stat *statb)
+{
+#ifdef HAVE_STRUCT_STAT_ST_MTIM
+ /* No nanosecond component to parse */
+ if (getenv("SOURCE_DATE_EPOCH"))
+ return 0;
+ return statb->st_mtim.tv_nsec;
+#else
+ return 0;
+#endif
+}
+
/*
* Validate a cache file by reading the header and checking
* the magic number and the size field
@@ -1033,10 +1082,10 @@ FcDirCacheValidateHelper (FcConfig *config, int fd, struct stat *fd_stat, struct
ret = FcFalse;
else if (fd_stat->st_size != c.size)
ret = FcFalse;
- else if (c.checksum != (int) dir_stat->st_mtime)
+ else if (c.checksum != FcDirChecksum (dir_stat))
ret = FcFalse;
#ifdef HAVE_STRUCT_STAT_ST_MTIM
- else if (c.checksum_nano != dir_stat->st_mtim.tv_nsec)
+ else if (c.checksum_nano != FcDirChecksumNano (dir_stat))
ret = FcFalse;
#endif
return ret;
@@ -1112,10 +1161,8 @@ FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, struct stat *dir_stat, FcSt
cache->magic = FC_CACHE_MAGIC_ALLOC;
cache->version = FC_CACHE_VERSION_NUMBER;
cache->size = serialize->size;
- cache->checksum = (int) dir_stat->st_mtime;
-#ifdef HAVE_STRUCT_STAT_ST_MTIM
- cache->checksum_nano = dir_stat->st_mtim.tv_nsec;
-#endif
+ cache->checksum = FcDirChecksum (dir_stat);
+ cache->checksum_nano = FcDirChecksumNano (dir_stat);
/*
* Serialize directory name