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,