From ab2ab441f7df31fa161a038042cdef92b54db416 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Thu, 27 Aug 2026 17:19:53 +0000 Subject: [PATCH] mkfs-util: forward a validated $SOURCE_DATE_EPOCH to mkfs.* The previous commit uses `parse_source_date_epoch()` for creating FAT time stamps. Do the same in `mtools_exec()`, `mkfs.*`, and `$E2FSPROGS_FAKE_TIME` instead of passing the raw environment variable. That avoids these programs creating time stamps from a value we would have rejected ourselves (unparsable, overflowing), read differently (e.g. octal numbers), or from one that `secure_getenv()` declines to look at under AT_SECURE. Note: mtools derives file and directory timestamps from $SOURCE_DATE_EPOCH since v4.0.20 (2018). --- src/shared/mkfs-util.c | 52 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 35955eb00b..cce6b6d55d 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -5,6 +5,7 @@ #include #include "escape.h" +#include "install-file.h" #include "log.h" #include "memory-util.h" #include "mkfs-util.h" @@ -17,6 +18,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "utf8.h" int mkfs_exists(const char *fstype) { @@ -150,6 +152,26 @@ static int mangle_fat_label(const char *s, char **ret) { return 0; } +/* $SOURCE_DATE_EPOCH in seconds as *we* read it, or NULL if we have none. The tools we call parse the + * variable themselves, so without this they would act on values we reject (unparsable, overflowing), + * read differently (octal 017), or that secure_getenv() declines to look at under AT_SECURE. */ +static int source_date_epoch_seconds(char **ret) { + usec_t epoch; + + assert(ret); + + epoch = parse_source_date_epoch(); + if (epoch == USEC_INFINITY) { + *ret = NULL; + return 0; + } + + if (asprintf(ret, USEC_FMT, epoch / USEC_PER_SEC) < 0) + return -ENOMEM; + + return 1; +} + static int mtools_exec(char *const *argv, ForkFlags extra_fork_flags) { int r; @@ -161,6 +183,16 @@ static int mtools_exec(char *const *argv, ForkFlags extra_fork_flags) { log_debug("Invoking mtools command: %s", strna(j)); } + _cleanup_free_ char *seconds = NULL, *source_date_epoch = NULL; + r = source_date_epoch_seconds(&seconds); + if (r < 0) + return log_oom(); + if (seconds) { + source_date_epoch = strjoin("SOURCE_DATE_EPOCH=", seconds); + if (!source_date_epoch) + return log_oom(); + } + r = pidref_safe_fork( "(mtools)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS|extra_fork_flags, @@ -174,7 +206,7 @@ static int mtools_exec(char *const *argv, ForkFlags extra_fork_flags) { execve(argv[0], argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1", "TZ=UTC", - strv_find_prefix(environ, "SOURCE_DATE_EPOCH="))); + source_date_epoch)); log_full_errno(FLAGS_SET(extra_fork_flags, FORK_LOG) ? LOG_ERR : LOG_DEBUG, errno, "Failed to execute %s: %m", argv[0]); _exit(EXIT_FAILURE); @@ -430,8 +462,11 @@ int make_filesystem( * 0 value handling, where $E2FSPROGS_FAKE_TIME=0 is ignored and the current time is used, * but $SOURCE_DATE_EPOCH=0 sets 1970-01-01 as the timestamp. */ if (!secure_getenv("E2FSPROGS_FAKE_TIME")) { /* honor $E2FSPROGS_FAKE_TIME if already set */ - const char *e = secure_getenv("SOURCE_DATE_EPOCH"); - if (e && strv_extend_strv(&env, STRV_MAKE("E2FSPROGS_FAKE_TIME", e), /* filter_duplicates= */ false) < 0) + _cleanup_free_ char *seconds = NULL; + + if (source_date_epoch_seconds(&seconds) < 0) + return log_oom(); + if (seconds && strv_extend_strv(&env, STRV_MAKE("E2FSPROGS_FAKE_TIME", seconds), /* filter_duplicates= */ false) < 0) return log_oom(); } @@ -650,6 +685,10 @@ int make_filesystem( fork_flags |= FORK_NEW_MOUNTNS; } + _cleanup_free_ char *source_date_epoch = NULL; + if (source_date_epoch_seconds(&source_date_epoch) < 0) + return log_oom(); + log_info("Formatting %s as %s", node, fstype); if (DEBUG_LOGGING) { @@ -675,6 +714,13 @@ int make_filesystem( _exit(EXIT_FAILURE); } + /* Same as in mtools_exec(): the mkfs tool gets our reading of the variable, or none. */ + if (source_date_epoch ? setenv("SOURCE_DATE_EPOCH", source_date_epoch, /* replace= */ true) < 0 + : unsetenv("SOURCE_DATE_EPOCH") < 0) { + log_error_errno(errno, "Failed to adjust $SOURCE_DATE_EPOCH: %m"); + _exit(EXIT_FAILURE); + } + /* mkfs.btrfs refuses to operate on block devices with mounted partitions, even if operating * on unformatted free space, so let's trick it and other mkfs tools into thinking no * partitions are mounted. See https://github.com/kdave/btrfs-progs/issues/640 for more