git: f6ed43d346b1 - stable/14 - mkimg: Make output reproducible using a specific timestamp

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 29 Jul 2025 12:49:10 UTC
The branch stable/14 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=f6ed43d346b1a79f62e33e3a3fe001d3fc5bcbde

commit f6ed43d346b1a79f62e33e3a3fe001d3fc5bcbde
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-07-07 20:27:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-07-29 12:08:32 +0000

    mkimg: Make output reproducible using a specific timestamp
    
    In commit 95ac5b0e27e9 ("mkimg: Add a reproducible mode"), I added a -R
    flag to mkimg, indicating that output should be reproducible.
    
    There is one place in the VHD image backend that uses time(3) to
    populate an image metadata field; this field is visible to tools which
    know how to process VHD images.  Rather than picking an arbitrary value
    for that timestamp, it seems better to behave like makefs(8) and let the
    invoker pick a suitable timestamp.
    
    So, remove -R and instead add a -t option which lets the timestamp be
    specified directly.  Then, modify the VHD backend to use that timestamp.
    
    Fixes:          95ac5b0e27e9 ("mkimg: Add a reproducible mode")
    Reviewed by:    bnovkov
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D51077
    
    (cherry picked from commit 393fefa3d9c78948d911e5de0182a9b858b23475)
---
 usr.bin/mkimg/mkimg.1 |  8 ++++----
 usr.bin/mkimg/mkimg.c | 24 ++++++++++++++++--------
 usr.bin/mkimg/mkimg.h |  4 ++--
 usr.bin/mkimg/uuid.c  |  7 ++++---
 usr.bin/mkimg/vhd.c   |  2 +-
 5 files changed, 27 insertions(+), 18 deletions(-)

diff --git a/usr.bin/mkimg/mkimg.1 b/usr.bin/mkimg/mkimg.1
index f6b151d2d5c7..ae48904eb16c 100644
--- a/usr.bin/mkimg/mkimg.1
+++ b/usr.bin/mkimg/mkimg.1
@@ -41,7 +41,7 @@
 .Op Fl f Ar format
 .Op Fl o Ar outfile
 .Op Fl a Ar active
-.Op Fl R
+.Op Fl t Ar timestamp
 .Op Fl v
 .Op Fl y
 .Op Fl s Ar scheme Op Fl p Ar partition ...
@@ -139,9 +139,9 @@ option is a shorthand to specify the minimum and maximum capacity at the
 same time.
 .Pp
 The
-.Fl R
-option enables reproducible mode: any timestamps or random identifiers will
-be fixed so as to ensure consistent output.
+.Fl t
+option causes any timestamps embedded in the output file to be set to the
+given time, specified in seconds since the epoch.
 .Pp
 The
 .Fl v
diff --git a/usr.bin/mkimg/mkimg.c b/usr.bin/mkimg/mkimg.c
index 58bf90fd5ad4..80c79777575e 100644
--- a/usr.bin/mkimg/mkimg.c
+++ b/usr.bin/mkimg/mkimg.c
@@ -62,7 +62,8 @@ static struct option longopts[] = {
 static uint64_t min_capacity = 0;
 static uint64_t max_capacity = 0;
 
-bool reproducible = false;
+/* Fixed timestamp for reproducible builds. */
+time_t timestamp = (time_t)-1;
 
 struct partlisthead partlist = TAILQ_HEAD_INITIALIZER(partlist);
 u_int nparts = 0;
@@ -564,7 +565,7 @@ main(int argc, char *argv[])
 
 	bcfd = -1;
 	outfd = 1;	/* Write to stdout by default */
-	while ((c = getopt_long(argc, argv, "a:b:c:C:f:o:p:s:vyH:P:RS:T:",
+	while ((c = getopt_long(argc, argv, "a:b:c:C:f:o:p:s:t:vyH:P:S:T:",
 	    longopts, NULL)) != -1) {
 		switch (c) {
 		case 'a':	/* ACTIVE PARTITION, if supported */
@@ -609,9 +610,6 @@ main(int argc, char *argv[])
 			if (error)
 				errc(EX_DATAERR, error, "partition");
 			break;
-		case 'R':
-			reproducible = true;
-			break;
 		case 's':	/* SCHEME */
 			if (scheme_selected() != NULL)
 				usage("multiple schemes given");
@@ -619,6 +617,19 @@ main(int argc, char *argv[])
 			if (error)
 				errc(EX_DATAERR, error, "scheme");
 			break;
+		case 't': {
+			char *ep;
+			long long val;
+
+			errno = 0;
+			val = strtoll(optarg, &ep, 0);
+			if (ep == optarg || *ep != '\0')
+				errno = EINVAL;
+			if (errno != 0)
+				errc(EX_DATAERR, errno, "timestamp");
+			timestamp = (time_t)val;
+			break;
+		}
 		case 'y':
 			unit_testing++;
 			break;
@@ -681,9 +692,6 @@ main(int argc, char *argv[])
 	if (max_capacity != 0 && min_capacity > max_capacity)
 		usage("minimum capacity cannot be larger than the maximum one");
 
-	if (reproducible)
-		srandom(42);
-
 	if (secsz > blksz) {
 		if (blksz != 0)
 			errx(EX_DATAERR, "the physical block size cannot "
diff --git a/usr.bin/mkimg/mkimg.h b/usr.bin/mkimg/mkimg.h
index 608de458e83c..aa0ec2a8d944 100644
--- a/usr.bin/mkimg/mkimg.h
+++ b/usr.bin/mkimg/mkimg.h
@@ -29,9 +29,9 @@
 
 #include <sys/queue.h>
 #include <sys/types.h>
-#include <stdbool.h>
+#include <time.h>
 
-extern bool reproducible;	/* Generate reproducible output. */
+extern time_t timestamp;
 
 struct part {
 	TAILQ_ENTRY(part) link;
diff --git a/usr.bin/mkimg/uuid.c b/usr.bin/mkimg/uuid.c
index f3415a8c1111..885a6c36b522 100644
--- a/usr.bin/mkimg/uuid.c
+++ b/usr.bin/mkimg/uuid.c
@@ -57,9 +57,10 @@ osdep_uuidgen(mkimg_uuid_t *uuid)
 	u_int i;
 	uint16_t seq;
 
-	if (reproducible)
-		memset(&tv, 0, sizeof(tv));
-	else if (gettimeofday(&tv, NULL) == -1)
+	if (timestamp != (time_t)-1) {
+		tv.tv_sec = timestamp;
+		tv.tv_usec = 0;
+	} else if (gettimeofday(&tv, NULL) == -1)
 		abort();
 
 	time += (uint64_t)tv.tv_sec * 10000000LL;
diff --git a/usr.bin/mkimg/vhd.c b/usr.bin/mkimg/vhd.c
index 1e1f1e7f3c3e..c0fe45ab416e 100644
--- a/usr.bin/mkimg/vhd.c
+++ b/usr.bin/mkimg/vhd.c
@@ -188,7 +188,7 @@ vhd_timestamp(void)
 	time_t t;
 
 	if (!unit_testing) {
-		t = time(NULL);
+		t = timestamp != (time_t)-1 ? timestamp : time(NULL);
 		return (t - 0x386d4380);
 	}