[BUG] e2fsck: ignores SOURCE_DATE_EPOCH / E2FSPROGS_FAKE_TIME for s_lastcheck
Levi Shafter <[email protected]>
| Newsgroups | org.kernel.vger.linux-ext4 |
|---|---|
| Message-ID | <[email protected]> |
Sponsor: 21SoftWare LLC
Version: e2fsprogs 1.47.1 (also 1.47.0)
Summary
mke2fs and the ext2fs library honor SOURCE_DATE_EPOCH (and
E2FSPROGS_FAKE_TIME) for filesystem timestamps, but e2fsck does not. A
check pass therefore writes the current wall-clock time into the
superblock's last-check time (s_lastcheck) and last-write time
(s_wtime), so running e2fsck as part of a reproducible image build
produces a different filesystem on every run.
Details
lib/ext2fs/openfs.c sets fs->now from SOURCE_DATE_EPOCH /
E2FSPROGS_FAKE_TIME when opening a filesystem. e2fsck, however,
initializes its
own clock without consulting those variables and then uses it for the
superblock:
- e2fsck/e2fsck.c (e2fsck_allocate_context): context->now =
getenv("E2FSCK_TIME") ? strtoull(...) : time(0);
- e2fsck/unix.c: fs->now = ctx->now; (overwrites the library's value)
- e2fsck/unix.c: ext2fs_set_tstamp(sb, s_lastcheck, ctx->now);
An E2FSCK_TIME override already exists, so the mechanism is in place —
it's just not tied to the reproducibility variables the rest of the
suite uses.
Steps to Reproduce
dd if=/dev/zero of=test.img bs=1M count=16
SOURCE_DATE_EPOCH=1000000000 mke2fs -t ext4 -F -q test.img
SOURCE_DATE_EPOCH=1000000000 e2fsck -fy test.img >/dev/null
dumpe2fs -h test.img | grep -E 'Filesystem created|Last checked|Last write'
Actual: Filesystem created = 2001-09-09 (mke2fs honored the epoch), but
Last checked / Last write time = the current date (e2fsck used
time(0)).
Expected: with SOURCE_DATE_EPOCH set, all three reflect the epoch.
Impact
Reproducible-build pipelines (e.g. Yocto/OpenEmbedded) that run fsck
during image creation get non-reproducible ext2/3/4 images.
OpenEmbedded's wic currently works around this with a post-fsck debugfs
set_super_value pass.
Suggested fix
In e2fsck_allocate_context, when E2FSCK_TIME is unset, fall back to
SOURCE_DATE_EPOCH (and/or E2FSPROGS_FAKE_TIME) before time(0), matching
lib/ext2fs. Roughly:
time_env = getenv("E2FSCK_TIME");
if (!time_env)
time_env = getenv("SOURCE_DATE_EPOCH");
if (time_env)
context->now = (time_t) strtoull(time_env, NULL, 0);
else {
context->now = time(0);
if (context->now < 1262322000) /* January 1 2010 */
context->flags |= E2F_FLAG_TIME_INSANE;
}