]> git.ipfire.org Git - thirdparty/openembedded/openembedded-core.git/commitdiff
image_types: make oe_mkext234fs reproducible
authorLevi Shafter <levi.shafter@elder-tomes.com>
Sat, 8 Aug 2026 18:11:51 +0000 (12:11 -0600)
committerRichard Purdie <richard.purdie@linuxfoundation.org>
Mon, 10 Aug 2026 16:35:44 +0000 (17:35 +0100)
oe_mkext234fs() creates ext2/3/4 images with mke2fs and then runs
"fsck -pvfD". Unlike the ext4 partitions produced by wic
(scripts/lib/wic/partition.py), this direct IMAGE_CMD path embeds build
time into the result and is not reproducible:

  - mke2fs picks a random directory hash seed, and the fsck "-D" pass
    reorders every directory using it;
  - mke2fs and e2fsck stamp the superblock mkfs/write/last-check times
    with the wall clock.

When SOURCE_DATE_EPOCH is set, apply the same handling wic already uses
for its ext4 partitions:

  - export E2FSPROGS_FAKE_TIME and pass a deterministic "-E hash_seed"
    derived from SOURCE_DATE_EPOCH (reusing wic's namespace UUID);
  - after mkfs+fsck, normalize the superblock time fields with debugfs,
    since e2fsck stamps wtime/lastcheck with the current time even under
    E2FSPROGS_FAKE_TIME (debugfs takes epochs with a leading '@').

The filesystem UUID is still assigned by mke2fs and is left for the user
to pin (e.g. "-U ..." via EXTRA_IMAGECMD), matching wic where the UUID
comes from the .wks.

Verified with comparison via xdelta3, getfattr, find, debugfs, and
dumpe2fs between two custom builds using the patch against two custom
builds omitting the patch. Builds were successful, and no delta was
observed due to the usage of fsck.

[YOCTO #16110]

Signed-off-by: Levi Shafter <lshafter@elder-tomes.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
meta/classes-recipe/image_types.bbclass

index ca137292257dc5359a538a1f762878eb2e548a61..c0b3a780131e9b61719892bd8636570cd6a8fd6c 100644 (file)
@@ -72,6 +72,17 @@ IMAGE_CMD:jffs2 = "mkfs.jffs2 --root=${IMAGE_ROOTFS} --faketime --output=${IMGDE
 
 IMAGE_CMD:cramfs = "mkfs.cramfs ${IMAGE_ROOTFS} ${IMGDEPLOYDIR}/${IMAGE_NAME}.cramfs ${EXTRA_IMAGECMD}"
 
+# Derive a deterministic directory hash seed from SOURCE_DATE_EPOCH so
+# reproducible builds get stable ext2/3/4 directory indexes
+def oe_ext234_hash_seed(d):
+    sde = d.getVar('SOURCE_DATE_EPOCH')
+    if not sde:
+        return ''
+    import uuid
+    return str(uuid.uuid5(uuid.UUID('e7429877-e7b3-4a68-a5c9-2f2fdf33d460'), sde))
+
+REPRODUCIBLE_EXT234_HASH_SEED ?= "${@oe_ext234_hash_seed(d)}"
+
 oe_mkext234fs () {
        fstype=$1
        extra_imagecmd=""
@@ -81,6 +92,14 @@ oe_mkext234fs () {
                extra_imagecmd=$@
        fi
 
+       # For reproducible builds, make mke2fs/e2fsck deterministic: a fixed time and
+       # directory hash seed (the fsck "-D" pass reorders directories using it).
+       # This mirrors what wic does for its ext4 partitions.
+       if [ -n "$SOURCE_DATE_EPOCH" ]; then
+               export E2FSPROGS_FAKE_TIME="$SOURCE_DATE_EPOCH"
+               extra_imagecmd="$extra_imagecmd -E hash_seed=${REPRODUCIBLE_EXT234_HASH_SEED}"
+       fi
+
        # If generating an empty image the size of the sparse block should be large
        # enough to allocate an ext4 filesystem using 4096 bytes per inode, this is
        # about 60K, so dd needs a minimum count of 60, with bs=1024 (bytes per IO)
@@ -98,6 +117,19 @@ oe_mkext234fs () {
        mkfs.$fstype -F $extra_imagecmd ${IMGDEPLOYDIR}/${IMAGE_NAME}.$fstype -d ${IMAGE_ROOTFS}
        # Error codes 0-3 indicate successfull operation of fsck (no errors or errors corrected)
        fsck.$fstype -pvfD ${IMGDEPLOYDIR}/${IMAGE_NAME}.$fstype || [ $? -le 3 ]
+
+       # e2fsck stamps the superblock write/last-check times with the current time
+       # even under E2FSPROGS_FAKE_TIME, so normalize every superblock time field
+       if [ -n "$SOURCE_DATE_EPOCH" ]; then
+               printf '%s\n' \
+                       "set_super_value mkfs_time @$SOURCE_DATE_EPOCH" \
+                       "set_super_value wtime @$SOURCE_DATE_EPOCH" \
+                       "set_super_value lastcheck @$SOURCE_DATE_EPOCH" \
+                       "set_super_value mtime @0" \
+                       "set_super_value first_error_time @0" \
+                       "set_super_value last_error_time @0" \
+                       | debugfs -w -f - ${IMGDEPLOYDIR}/${IMAGE_NAME}.$fstype
+       fi
 }
 
 IMAGE_CMD:ext2 = "oe_mkext234fs ext2 ${EXTRA_IMAGECMD}"