Bug#864082: fontconfig: please make the cache files reproducible

Chris Lamb lamby at debian.org
Sun Jun 4 08:13:09 UTC 2017


Source: fontconfig
Version: 2.12.1-0.1
Severity: wishlist
Tags: patch
User: reproducible-builds at lists.alioth.debian.org
Usertags: timestamps
X-Debbugs-Cc: reproducible-bugs at lists.alioth.debian.org

Hi,

Whilst working on the Reproducible Builds effort [0], we noticed that
fontconfig generates non-reproducible cache files under
/var/cache/fontconfig.

This is because fontconfig embeds the mtime of each font directory
in a "checksum" member of a "_FcCache" struct. This is so that it
can identify which cache files remain valid and/or require
regeneration.

We therefore "clamp" the mtimes of font directories to SOURCE_DATE_EPOCH
prior to calling fc-cache to avoid these non-deterministic values
appearing in the files themselves. This is safe as we now force
regeneration in subsequent fc-cache calls with -f.

(We can't just replace the checksum value with SOURCE_DATE_EPOCH as it
will result in fontconfig believing the cache to be outdated, defeating
the entire point of generating them in the first place.)

This work was sponsored by Tails[1].

Patch attached.


 [0] https://reproducible-builds.org/
 [1] https://tails.boum.org/


Regards,

-- 
      ,''`.
     : :'  :     Chris Lamb
     `. `'`      lamby at debian.org / chris-lamb.co.uk
       `-
-------------- next part --------------
diff --git a/debian/fontconfig.postinst b/debian/fontconfig.postinst
index ad7ac19..dfba70e 100644
--- a/debian/fontconfig.postinst
+++ b/debian/fontconfig.postinst
@@ -2,10 +2,28 @@
 
 set -e
 
+if [ -n "$SOURCE_DATE_EPOCH" ]; then
+  # fontconfig embeds the mtime of each font directory in a "checksum" member
+  # of a "_FcCache" struct. This is so that it can identify which cache files
+  # remain valid and/or require regeneration.
+  #
+  # We therefore "clamp" the mtimes of font directories to SOURCE_DATE_EPOCH
+  # prior to calling fc-cache to avoid these non-deterministic values appearing
+  # in the files themselves. This is safe as we force regeneration in
+  # subsequent fc-cache calls with -f.
+  #
+  # (We can't just replace the checksum value with SOURCE_DATE_EPOCH as it will
+  # result in fontconfig believing the cache to be outdated, defeating the
+  # entire point of generating them in the first place.
+  fc-cache -s --list-dirs | \
+    xargs -I{} find {} -type d -follow -newermt "@$SOURCE_DATE_EPOCH" -print0 2>/dev/null | \
+    xargs -0r touch --date="@$SOURCE_DATE_EPOCH"
+fi
+
 if [ "$1" = triggered ]; then
   # Force regeneration of all fontconfig cache files.
   mkdir -p /var/cache/fontconfig
-  fc-cache -s -v 1>/var/log/fontconfig.log 2>&1 || printf "fc-cache failed.\nSee /var/log/fontconfig.log for more information.\n"
+  fc-cache -s -f -v 1>/var/log/fontconfig.log 2>&1 || printf "fc-cache failed.\nSee /var/log/fontconfig.log for more information.\n"
   exit 0
 fi
 
diff --git a/fc-cache/fc-cache.1 b/fc-cache/fc-cache.1
index e514779..f5a733d 100644
--- a/fc-cache/fc-cache.1
+++ b/fc-cache/fc-cache.1
@@ -4,7 +4,7 @@
 fc-cache \- build font information cache files
 .SH SYNOPSIS
 .sp
-\fBfc-cache\fR [ \fB-EfrsvVh\fR ]  [ \fB--error-on-no-fonts\fR ]  [ \fB--force\fR ]  [ \fB--really-force\fR ]  [ \fB [ -y \fIdir\fB ]  [ --sysroot \fIdir\fB ] \fR ]  [ \fB--system-only\fR ]  [ \fB--verbose\fR ]  [ \fB--version\fR ]  [ \fB--help\fR ]  [ \fB\fIdir\fB\fR\fI...\fR ] 
+\fBfc-cache\fR [ \fB-EfrsvVh\fR ]  [ \fB--error-on-no-fonts\fR ]  [ \fB--force\fR ]  [ \fB--really-force\fR ]  [ \fB [ -y \fIdir\fB ]  [ --sysroot \fIdir\fB ] \fR ]  [ \fB--system-only\fR ]  [ \fB--list-dirs\fR ]  [ \fB--verbose\fR ]  [ \fB--version\fR ]  [ \fB--help\fR ]  [ \fB\fIdir\fB\fR\fI...\fR ] 
 .SH "DESCRIPTION"
 .PP
 \fBfc-cache\fR scans the font directories on
@@ -44,6 +44,9 @@ Erase all existing cache files and rescan.
 Only scan system-wide directories, omitting the places
 located in the user's home directory.
 .TP
+\fB-l\fR
+Only list directories, don't regenerate anything.
+.TP
 \fB-v\fR
 Display status information while busy.
 .TP
diff --git a/fc-cache/fc-cache.c b/fc-cache/fc-cache.c
index 0336073..fc5ff07 100644
--- a/fc-cache/fc-cache.c
+++ b/fc-cache/fc-cache.c
@@ -70,6 +70,7 @@ const struct option longopts[] = {
     {"really-force", 0, 0, 'r'},
     {"sysroot", required_argument, 0, 'y'},
     {"system-only", 0, 0, 's'},
+    {"list-dirs", 0, 0, 'l'},
     {"version", 0, 0, 'V'},
     {"verbose", 0, 0, 'v'},
     {"help", 0, 0, 'h'},
@@ -87,10 +88,10 @@ usage (char *program, int error)
 {
     FILE *file = error ? stderr : stdout;
 #if HAVE_GETOPT_LONG
-    fprintf (file, "usage: %s [-EfrsvVh] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--verbose] [--version] [--help] [dirs]\n",
+    fprintf (file, "usage: %s [-EfrslvVh] [-y SYSROOT] [--error-on-no-fonts] [--force|--really-force] [--sysroot=SYSROOT] [--system-only] [--list-dirs] [--verbose] [--version] [--help] [dirs]\n",
 	     program);
 #else
-    fprintf (file, "usage: %s [-EfrsvVh] [-y SYSROOT] [dirs]\n",
+    fprintf (file, "usage: %s [-EfrslvVh] [-y SYSROOT] [dirs]\n",
 	     program);
 #endif
     fprintf (file, "Build font information caches in [dirs]\n"
@@ -102,6 +103,7 @@ usage (char *program, int error)
     fprintf (file, "  -r, --really-force       erase all existing caches, then rescan\n");
     fprintf (file, "  -s, --system-only        scan system-wide directories only\n");
     fprintf (file, "  -y, --sysroot=SYSROOT    prepend SYSROOT to all paths for scanning\n");
+    fprintf (file, "  -l, --list-dirs          list directories only\n");
     fprintf (file, "  -v, --verbose            display status information while busy\n");
     fprintf (file, "  -V, --version            display font config version and exit\n");
     fprintf (file, "  -h, --help               display this help and exit\n");
@@ -112,6 +114,7 @@ usage (char *program, int error)
     fprintf (file, "  -r,   (really force) erase all existing caches, then rescan\n");
     fprintf (file, "  -s         (system)  scan system-wide directories only\n");
     fprintf (file, "  -y SYSROOT (sysroot) prepend SYSROOT to all paths for scanning\n");
+    fprintf (file, "  -l         (list-dirs) list directories only\n");
     fprintf (file, "  -v         (verbose) display status information while busy\n");
     fprintf (file, "  -V         (version) display font config version and exit\n");
     fprintf (file, "  -h         (help)    display this help and exit\n");
@@ -282,19 +285,21 @@ main (int argc, char **argv)
     FcBool	force = FcFalse;
     FcBool	really_force = FcFalse;
     FcBool	systemOnly = FcFalse;
+    FcBool	listDirs = FcFalse;
     FcBool	error_on_no_fonts = FcFalse;
     FcConfig	*config;
     FcChar8     *sysroot = NULL;
+    FcChar8     *dir;
     int		i;
     int		changed;
-    int		ret;
+    int		ret = 0;
 #if HAVE_GETOPT_LONG || HAVE_GETOPT
     int		c;
 
 #if HAVE_GETOPT_LONG
-    while ((c = getopt_long (argc, argv, "Efrsy:Vvh", longopts, NULL)) != -1)
+    while ((c = getopt_long (argc, argv, "Efrsly:Vvh", longopts, NULL)) != -1)
 #else
-    while ((c = getopt (argc, argv, "Efrsy:Vvh")) != -1)
+    while ((c = getopt (argc, argv, "Efrsly:Vvh")) != -1)
 #endif
     {
 	switch (c) {
@@ -313,6 +318,9 @@ main (int argc, char **argv)
 	case 'y':
 	    sysroot = FcStrCopy ((const FcChar8 *)optarg);
 	    break;
+	case 'l':
+	    listDirs = FcTrue;
+	    break;
 	case 'V':
 	    fprintf (stderr, "fontconfig version %d.%d.%d\n", 
 		     FC_MAJOR, FC_MINOR, FC_REVISION);
@@ -374,6 +382,13 @@ main (int argc, char **argv)
     else
 	list = FcConfigGetConfigDirs (config);
 
+    if (listDirs)
+    {
+	while ((dir = FcStrListNext (list)))
+	    printf ("%s\n", dir);
+	goto done;
+    }
+
     if ((processed_dirs = FcStrSetCreate()) == NULL) {
 	fprintf(stderr, "Cannot malloc\n");
 	return 1;
@@ -394,6 +409,7 @@ main (int argc, char **argv)
 
     cleanCacheDirectories (config, verbose);
 
+done:
     FcConfigDestroy (config);
     FcFini ();
     /* 
diff --git a/fc-cache/fc-cache.sgml b/fc-cache/fc-cache.sgml
index 5ae0107..4430010 100644
--- a/fc-cache/fc-cache.sgml
+++ b/fc-cache/fc-cache.sgml
@@ -72,6 +72,7 @@ manpage.1: manpage.sgml
 	<arg><option>--sysroot</option> <option><replaceable>dir</replaceable></option></arg>
       </group>
       <arg><option>--system-only</option></arg>
+      <arg><option>--list-dirs</option></arg>
       <arg><option>--verbose</option></arg>
       <arg><option>--version</option></arg>
       <arg><option>--help</option></arg>
@@ -144,6 +145,14 @@ manpage.1: manpage.sgml
         </listitem>
       </varlistentry>
       <varlistentry>
+        <term><option>-l</option>
+          <option>--list-dirs</option>
+        </term>
+        <listitem>
+	  <para>Only list directories, don't regenerate anything.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
         <term><option>-v</option>
           <option>--verbose</option>
         </term>


More information about the Reproducible-bugs mailing list