Bug#1004557: man-db: please make index.db installations reproducible

Johannes Schauer Marin Rodrigues josch at debian.org
Sun Jan 30 13:27:05 GMT 2022


Source: man-db
Version: 2.9.4-4
Severity: normal
Tags: patch
User: reproducible-builds at lists.alioth.debian.org
Usertags: timestamps
X-Debbugs-Cc: josch at debian.org, reproducible-bugs at lists.alioth.debian.org

Hi,

currently, the index.db files created by man-db -c are unreproducible
when creating a Debian chroot. This means that tools that attempt to
create reproducible system images delete all index.db files:

https://gitlab.tails.boum.org/tails/tails/-/blob/stable/config/chroot_local-hooks/99-zzzzzz_reproducible-builds-post-processing#L28
https://salsa.debian.org/live-team/live-build/-/blob/master/share/hooks/normal/0190-remove-temporary-files.hook.chroot#L6

This could be avoided if the index.db files after installation would be
bit-by-bit reproducible. The attached patch fixes the problem by
truncating the timestamp set in index.db to the value of
SOURCE_DATE_EPOCH if the variable is set.

This means that this patch does not change anything during normal
operation but only comes into play if a utility that sets
SOURCE_DATE_EPOCH is installing packages.

Thanks!

cheers, josch
-------------- next part --------------
--- a/libdb/db_store.c
+++ b/libdb/db_store.c
@@ -29,6 +29,8 @@
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <errno.h>
+#include <limits.h>
 
 #include "timespec.h"
 #include "xvasprintf.h"
@@ -134,13 +136,45 @@ static datum make_content (struct mandat
 	if (!in->whatis)
 		in->whatis = dash + 1;
 
+	struct timespec ts = { .tv_sec = in->mtime.tv_sec, .tv_nsec = in->mtime.tv_nsec };
+	char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+	unsigned long long epoch;
+	char *endptr;
+	if (source_date_epoch) {
+		// if SOURCE_DATE_EPOCH is set, replace the timestamp with it if
+		// SOURCE_DATE_EPOCH is smaller than the file timestamp
+		errno = 0;
+		epoch = strtoull(source_date_epoch, &endptr, 10);
+		if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
+			|| (errno != 0 && epoch == 0)) {
+			fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n", strerror(errno));
+			exit(EXIT_FAILURE);
+		}
+		if (endptr == source_date_epoch) {
+			fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n", endptr);
+			exit(EXIT_FAILURE);
+		}
+		if (*endptr != '\0') {
+			fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n", endptr);
+			exit(EXIT_FAILURE);
+		}
+		if (epoch > ULONG_MAX) {
+			fprintf(stderr, "Environment variable $SOURCE_DATE_EPOCH: value must be smaller than or equal to: %lu but was found to be: %llu \n", ULONG_MAX, epoch);
+			exit(EXIT_FAILURE);
+		}
+		if (ts.tv_sec > epoch || (ts.tv_sec == epoch && ts.tv_nsec > 0)) {
+			ts.tv_sec = epoch;
+			ts.tv_nsec = 0;
+		}
+	}
+
 	MYDBM_SET (cont, xasprintf (
 		"%s\t%s\t%s\t%ld\t%ld\t%c\t%s\t%s\t%s\t%s",
 		dash_if_unset (in->name),
 		in->ext,
 		in->sec,
-		(long) in->mtime.tv_sec,
-		(long) in->mtime.tv_nsec,
+		(long) ts.tv_sec,
+		(long) ts.tv_nsec,
 		in->id,
 		in->pointer,
 		in->filter,


More information about the Reproducible-bugs mailing list