From 55c59ef97ed1eb443491fbc2c09e4614960b0af7 Mon Sep 17 00:00:00 2001 From: Luke Bemish Date: Tue, 13 Jun 2023 06:17:20 -0400 Subject: [PATCH] Change zip entry ordering to place manifest first (#887) * Change zip entry ordering to place manifest first * Add handling for signature related files * Update jar file hash * Hopefully update correct file hashes this time * Change jar hashes * Add correct source hashes --- .../loom/util/ZipReprocessorUtil.java | 47 ++++++++++++++++++- .../integration/ReproducibleBuildTest.groovy | 12 ++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java index 43b28db5..79f0c872 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java +++ b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java @@ -45,6 +45,51 @@ public class ZipReprocessorUtil { private ZipReprocessorUtil() { } + private static final String MANIFEST_LOCATION = "META-INF/MANIFEST.MF"; + private static final String META_INF = "META-INF/"; + + // See https://docs.oracle.com/en/java/javase/20/docs/specs/jar/jar.html#signed-jar-file + private static boolean isSpecialFile(String zipEntryName) { + if (!zipEntryName.startsWith(META_INF)) { + return false; + } + + String[] parts = zipEntryName.split("/"); + + if (parts.length != 2) { + return false; + } + + return parts[1].startsWith("SIG-") + || parts[1].endsWith(".SF") + || parts[1].endsWith(".DSA") + || parts[1].endsWith(".RSA") + || parts[1].endsWith(".EC"); + } + + private static int specialOrdering(String name1, String name2) { + if (name1.equals(name2)) { + return 0; + } else if (name1.equals(MANIFEST_LOCATION)) { + return -1; + } else if (name2.equals(MANIFEST_LOCATION)) { + return 1; + } + + boolean isName1Special = isSpecialFile(name1); + boolean isName2Special = isSpecialFile(name2); + + if (isName1Special && isName2Special) { + return name1.compareTo(name2); + } else if (isName1Special) { + return -1; + } else if (isName2Special) { + return 1; + } + + return name1.compareTo(name2); + } + public static void reprocessZip(File file, boolean reproducibleFileOrder, boolean preserveFileTimestamps) throws IOException { if (!reproducibleFileOrder && preserveFileTimestamps) { return; @@ -54,7 +99,7 @@ public class ZipReprocessorUtil { ZipEntry[] entries; if (reproducibleFileOrder) { - entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName)).toArray(ZipEntry[]::new); + entries = zipFile.stream().sorted(Comparator.comparing(ZipEntry::getName, ZipReprocessorUtil::specialOrdering)).toArray(ZipEntry[]::new); } else { entries = zipFile.stream().toArray(ZipEntry[]::new); } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy index 22700065..f40a65e0 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/ReproducibleBuildTest.groovy @@ -55,13 +55,13 @@ class ReproducibleBuildTest extends Specification implements GradleProjectTestTr where: version | modHash | sourceHash - DEFAULT_GRADLE | "ed3306ef60f434c55048cba8de5dab95" | [ - "0d9eec9248d93eb6ec4a1cd4d927e609", - "436bf54ef015576b0a338d55d9a0bb82" + DEFAULT_GRADLE | "174c9b52f4bc6d489548d11b42e853cf" | [ + "5e6e56df303b4fbaaef372d6f143dbfc", + "92b6fbffd0bd14bf3c626750eb86c264" ] - PRE_RELEASE_GRADLE | "ed3306ef60f434c55048cba8de5dab95" | [ - "0d9eec9248d93eb6ec4a1cd4d927e609", - "436bf54ef015576b0a338d55d9a0bb82" + PRE_RELEASE_GRADLE | "174c9b52f4bc6d489548d11b42e853cf" | [ + "5e6e56df303b4fbaaef372d6f143dbfc", + "92b6fbffd0bd14bf3c626750eb86c264" ] }