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).
This commit is contained in:
Martin Pitt
2026-08-28 11:16:34 +02:00
parent 94bb5f8e4d
commit ab2ab441f7
+49 -3
View File
@@ -5,6 +5,7 @@
#include <unistd.h>
#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