]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mkfs-util: set hash_seed to seed derived value for reproducibility
authorZauberNerd <zaubernerd@zaubernerd.de>
Sun, 11 Jan 2026 14:32:33 +0000 (15:32 +0100)
committerMike Yuan <me@yhndnzj.com>
Mon, 12 Jan 2026 15:57:44 +0000 (16:57 +0100)
When creating ext2/ext3/ext4 filesystems, mke2fs generates a random
hash_seed for htree directory indexing. This causes non-reproducible
images even when SOURCE_DATE_EPOCH is set and the same filesystem UUID
is used.

Set the hash_seed explicitly to match the filesystem UUID, ensuring
that repeated builds with the same seed produce bit-for-bit identical
images.

Also add a test case in TEST-58-REPART to verify ext4 reproducibility
by creating the same partition twice and comparing the results.

See https://vdwaa.nl/mkosi-reproducible-arch-images.html

I used claude ai to help me with this change.

src/shared/mkfs-util.c
test/units/TEST-58-REPART.sh

index a550c59dbeaea3ea1a5f74746a1c48b601a26ddf..0c086b8adfd4628b51fe69355522455348a003c1 100644 (file)
@@ -421,12 +421,19 @@ int make_filesystem(
 
         /* When changing this conditional, also adjust the log statement below. */
         if (STR_IN_SET(fstype, "ext2", "ext3", "ext4")) {
+                const char *ext_e_opts;
+
+                /* Set hash_seed to the same value as the filesystem UUID for reproducibility */
+                ext_e_opts = strjoina(FLAGS_SET(flags, MKFS_DISCARD) ? "discard" : "nodiscard",
+                                      ",lazy_itable_init=1,hash_seed=",
+                                      vol_id);
+
                 argv = strv_new(mkfs,
                                 "-L", label,
                                 "-U", vol_id,
                                 "-I", "256",
                                 "-m", "0",
-                                "-E", FLAGS_SET(flags, MKFS_DISCARD) ? "discard,lazy_itable_init=1" : "nodiscard,lazy_itable_init=1",
+                                "-E", ext_e_opts,
                                 "-b", "4096",
                                 "-T", "default");
                 if (!argv)
index 386462aa26ed327aecf311b40df298fd6f205391..8484cbd1c1fdb08c5cecba82ad1f04a02742cf69 100755 (executable)
@@ -1899,6 +1899,46 @@ testcase_luks2_integrity() {
     _test_luks2_integrity "hmac-sha512"
 }
 
+testcase_ext_reproducibility() {
+    local defs imgs
+
+    # Online mode mounts the filesystem which updates inode timestamps non-deterministically
+    if [[ "$OFFLINE" != "yes" ]]; then
+        echo "Skipping ext reproducibility test in online mode."
+        return 0
+    fi
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+
+    tee "$defs/root.conf" <<EOF
+[Partition]
+Type=root
+Format=ext4
+EOF
+
+    # Build the image twice with the same seed and verify they are identical
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --empty=create \
+                   --size=50M \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/test1.img"
+
+    systemd-repart --offline="$OFFLINE" \
+                   --definitions="$defs" \
+                   --empty=create \
+                   --size=50M \
+                   --seed="$seed" \
+                   --dry-run=no \
+                   "$imgs/test2.img"
+
+    cmp "$imgs/test1.img" "$imgs/test2.img"
+}
+
 OFFLINE="yes"
 run_testcases