Source: man-db
Version: 2.9.4-4
Severity: normal
Tags: patch
User: reproducible-bui...@lists.alioth.debian.org
Usertags: timestamps
X-Debbugs-Cc: jo...@debian.org, reproducible-b...@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
--- 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,

Reply via email to