From 8da7bc93315cb0c32ad868f17808468b81fa76ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Forsman?= Date: Wed, 5 Dec 2018 19:52:51 +0100 Subject: [PATCH] Honor the SOURCE_DATE_EPOCH variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the SOURCE_DATE_EPOCH specification[1] for reproducible builds. If SOURCE_DATE_EPOCH is set, use it as timestamp instead of the current time. [1] https://reproducible-builds.org/specs/source-date-epoch/ Signed-off-by: Bjørn Forsman --- src/boot.c | 23 +++++++++++++++++++++-- src/common.c | 18 ++++++++++++++++-- src/mkfs.fat.c | 19 ++++++++++++++++--- 3 files changed, 53 insertions(+), 7 deletions(-) --- a/src/boot.c +++ b/src/boot.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "common.h" #include "fsck.fat.h" @@ -672,6 +674,7 @@ void write_volume_label(DOS_FS * fs, cha { time_t now; struct tm *mtime; + char *source_date_epoch = NULL; off_t offset; int created; DIR_ENT de; @@ -687,8 +690,24 @@ void write_volume_label(DOS_FS * fs, cha if (de.name[0] == 0xe5) de.name[0] = 0x05; - now = time(NULL); - mtime = (now != (time_t)-1) ? localtime(&now) : NULL; + source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + if (source_date_epoch) { + char *tmp = NULL; + long long conversion = 0; + errno = 0; + conversion = strtoll(source_date_epoch, &tmp, 10); + now = conversion; + if (!isdigit((unsigned char)*source_date_epoch) || *tmp != '\0' + || errno != 0 || (long long)now != conversion) { + die("SOURCE_DATE_EPOCH is too big or contains non-digits: \"%s\"", + source_date_epoch); + } + mtime = gmtime(&now); + } else { + now = time(NULL); + mtime = (now != (time_t)-1) ? localtime(&now) : NULL; + } + if (mtime && mtime->tm_year >= 80 && mtime->tm_year <= 207) { de.time = htole16((unsigned short)((mtime->tm_sec >> 1) + (mtime->tm_min << 5) + --- a/src/common.c +++ b/src/common.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -298,8 +299,21 @@ void check_atari(void) uint32_t generate_volume_id(void) { struct timeval now; + char *source_date_epoch = NULL; - if (gettimeofday(&now, NULL) != 0 || now.tv_sec == (time_t)-1 || now.tv_sec < 0) { + source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + if (source_date_epoch) { + char *tmp = NULL; + long long conversion = 0; + errno = 0; + conversion = strtoll(source_date_epoch, &tmp, 10); + if (!isdigit((unsigned char)*source_date_epoch) || *tmp != '\0' + || errno != 0) { + die("SOURCE_DATE_EPOCH is too big or contains non-digits: \"%s\"", + source_date_epoch); + } + return (uint32_t)conversion; + } else if (gettimeofday(&now, NULL) != 0 || now.tv_sec == (time_t)-1 || now.tv_sec < 0) { srand(getpid()); /* rand() returns int from [0,RAND_MAX], therefore only 31 bits */ return (((uint32_t)(rand() & 0xFFFF)) << 16) | ((uint32_t)(rand() & 0xFFFF)); --- a/src/mkfs.fat.c +++ b/src/mkfs.fat.c @@ -1074,7 +1074,7 @@ static void setup_tables(void) } /* If is not available then generate random 32 bit disk signature */ - if (invariant) + if (invariant || getenv("SOURCE_DATE_EPOCH")) disk_sig = volume_id; else if (!disk_sig) disk_sig = generate_volume_id(); @@ -1287,7 +1287,7 @@ static void setup_tables(void) de->name[0] = 0x05; de->attr = ATTR_VOLUME; if (create_time != (time_t)-1) { - if (!invariant) + if (!invariant && !getenv("SOURCE_DATE_EPOCH")) ctime = localtime(&create_time); else ctime = gmtime(&create_time); @@ -1477,6 +1477,7 @@ int main(int argc, char **argv) int blocks_specified = 0; struct timeval create_timeval; long long conversion; + char *source_date_epoch = NULL; enum {OPT_HELP=1000, OPT_INVARIANT, OPT_MBR, OPT_VARIANT, OPT_CODEPAGE, OPT_OFFSET}; const struct option long_options[] = { @@ -1497,8 +1498,20 @@ int main(int argc, char **argv) program_name = p + 1; } - if (gettimeofday(&create_timeval, NULL) == 0 && create_timeval.tv_sec != (time_t)-1) + source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + if (source_date_epoch) { + errno = 0; + conversion = strtoll(source_date_epoch, &tmp, 10); + create_time = conversion; + if (!isdigit((unsigned char)*source_date_epoch) || *tmp != '\0' + || errno != 0 || (long long)create_time != conversion) { + die("SOURCE_DATE_EPOCH is too big or contains non-digits: \"%s\"", + source_date_epoch); + } + } else if (gettimeofday(&create_timeval, NULL) == 0 && create_timeval.tv_sec != (time_t)-1) { create_time = create_timeval.tv_sec; + } + volume_id = generate_volume_id(); check_atari();