mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8353185: Introduce the concept of upgradeable files in context of JEP 493
Reviewed-by: clanger, ihse, alanb
This commit is contained in:
parent
d7676c39b6
commit
4e24dc003c
32
make/modules/jdk.jlink/Java.gmk
Normal file
32
make/modules/jdk.jlink/Java.gmk
Normal file
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation. Oracle designates this
|
||||
# particular file as subject to the "Classpath" exception as provided
|
||||
# by Oracle in the LICENSE file that accompanied this code.
|
||||
#
|
||||
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# version 2 for more details (a copy is included in the LICENSE file that
|
||||
# accompanied this code).
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License version
|
||||
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
|
||||
################################################################################
|
||||
|
||||
# Instruct SetupJavaCompilation for the jdk.jlink module to include
|
||||
# upgrade_files_<module-name>.conf files
|
||||
COPY += .conf
|
||||
|
||||
################################################################################
|
||||
@ -45,6 +45,7 @@ import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
@ -76,6 +77,7 @@ public class JRTArchive implements Archive {
|
||||
private final Map<String, ResourceDiff> resDiff;
|
||||
private final boolean errorOnModifiedFile;
|
||||
private final TaskHelper taskHelper;
|
||||
private final Set<String> upgradeableFiles;
|
||||
|
||||
/**
|
||||
* JRTArchive constructor
|
||||
@ -86,12 +88,15 @@ public class JRTArchive implements Archive {
|
||||
* install aborts the link.
|
||||
* @param perModDiff The lib/modules (a.k.a jimage) diff for this module,
|
||||
* possibly an empty list if there are no differences.
|
||||
* @param taskHelper The task helper instance.
|
||||
* @param upgradeableFiles The set of files that are allowed for upgrades.
|
||||
*/
|
||||
JRTArchive(String module,
|
||||
Path path,
|
||||
boolean errorOnModifiedFile,
|
||||
List<ResourceDiff> perModDiff,
|
||||
TaskHelper taskHelper) {
|
||||
TaskHelper taskHelper,
|
||||
Set<String> upgradeableFiles) {
|
||||
this.module = module;
|
||||
this.path = path;
|
||||
this.ref = ModuleFinder.ofSystem()
|
||||
@ -105,6 +110,7 @@ public class JRTArchive implements Archive {
|
||||
this.resDiff = Objects.requireNonNull(perModDiff).stream()
|
||||
.collect(Collectors.toMap(ResourceDiff::getName, Function.identity()));
|
||||
this.taskHelper = taskHelper;
|
||||
this.upgradeableFiles = upgradeableFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,7 +223,8 @@ public class JRTArchive implements Archive {
|
||||
|
||||
// Read from the base JDK image.
|
||||
Path path = BASE.resolve(m.resPath);
|
||||
if (shaSumMismatch(path, m.hashOrTarget, m.symlink)) {
|
||||
if (!isUpgradeableFile(m.resPath) &&
|
||||
shaSumMismatch(path, m.hashOrTarget, m.symlink)) {
|
||||
if (errorOnModifiedFile) {
|
||||
String msg = taskHelper.getMessage("err.runtime.link.modified.file", path.toString());
|
||||
IOException cause = new IOException(msg);
|
||||
@ -239,6 +246,17 @@ public class JRTArchive implements Archive {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Certain files in a module are considered upgradeable. That is,
|
||||
* their hash sums aren't checked.
|
||||
*
|
||||
* @param resPath The resource path of the file to check for upgradeability.
|
||||
* @return {@code true} if the file is upgradeable. {@code false} otherwise.
|
||||
*/
|
||||
private boolean isUpgradeableFile(String resPath) {
|
||||
return upgradeableFiles.contains(resPath);
|
||||
}
|
||||
|
||||
static boolean shaSumMismatch(Path res, String expectedSha, boolean isSymlink) {
|
||||
if (isSymlink) {
|
||||
return false;
|
||||
|
||||
@ -28,7 +28,10 @@ package jdk.tools.jlink.internal;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.tools.jlink.internal.runtimelink.ResourceDiff;
|
||||
|
||||
@ -42,6 +45,9 @@ public class LinkableRuntimeImage {
|
||||
public static final String RESPATH_PATTERN = "jdk/tools/jlink/internal/runtimelink/fs_%s_files";
|
||||
// The diff files per module for supporting linking from the run-time image
|
||||
public static final String DIFF_PATTERN = "jdk/tools/jlink/internal/runtimelink/diff_%s";
|
||||
// meta data for upgradable files
|
||||
private static final String UPGRADEABLE_FILES_PATTERN = "jdk/tools/jlink/internal/runtimelink/upgrade_files_%s.conf";
|
||||
private static final Module JDK_JLINK_MOD = LinkableRuntimeImage.class.getModule();
|
||||
|
||||
/**
|
||||
* In order to be able to show whether or not a runtime is capable of
|
||||
@ -62,7 +68,38 @@ public class LinkableRuntimeImage {
|
||||
|
||||
private static InputStream getDiffInputStream(String module) throws IOException {
|
||||
String resourceName = String.format(DIFF_PATTERN, module);
|
||||
return LinkableRuntimeImage.class.getModule().getResourceAsStream(resourceName);
|
||||
return JDK_JLINK_MOD.getResourceAsStream(resourceName);
|
||||
}
|
||||
|
||||
private static Set<String> upgradeableFiles(String module) {
|
||||
String resourceName = String.format(UPGRADEABLE_FILES_PATTERN, module);
|
||||
InputStream filesIn = null;
|
||||
try {
|
||||
filesIn = JDK_JLINK_MOD.getResourceAsStream(resourceName);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IO error getting res stream");
|
||||
}
|
||||
if (filesIn == null) {
|
||||
// no upgradeable files
|
||||
return Set.of();
|
||||
}
|
||||
Set<String> upgradeableFiles = new HashSet<>();
|
||||
final InputStream in = filesIn;
|
||||
try (in;
|
||||
Scanner scanner = new Scanner(filesIn)) {
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
if (line.trim().startsWith("#")) {
|
||||
// Skip comments
|
||||
continue;
|
||||
}
|
||||
upgradeableFiles.add(scanner.nextLine());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Failure to retrieve upgradeable files for " +
|
||||
"module " + module, e);
|
||||
}
|
||||
return upgradeableFiles;
|
||||
}
|
||||
|
||||
public static Archive newArchive(String module,
|
||||
@ -81,7 +118,13 @@ public class LinkableRuntimeImage {
|
||||
throw new AssertionError("Failure to retrieve resource diff for " +
|
||||
"module " + module, e);
|
||||
}
|
||||
return new JRTArchive(module, path, !ignoreModifiedRuntime, perModuleDiff, taskHelper);
|
||||
Set<String> upgradeableFiles = upgradeableFiles(module);
|
||||
return new JRTArchive(module,
|
||||
path,
|
||||
!ignoreModifiedRuntime,
|
||||
perModuleDiff,
|
||||
taskHelper,
|
||||
upgradeableFiles);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
# Configuration for resource paths of files allowed to be
|
||||
# upgraded (in java.base)
|
||||
lib/tzdb.dat
|
||||
lib/security/cacerts
|
||||
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import tests.Helper;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Verify that no errors are reported for files that have been
|
||||
* upgraded when linking from the run-time image
|
||||
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
|
||||
* @library ../../lib /test/lib
|
||||
* @modules java.base/jdk.internal.jimage
|
||||
* jdk.jlink/jdk.tools.jlink.internal
|
||||
* jdk.jlink/jdk.tools.jlink.plugin
|
||||
* jdk.jlink/jdk.tools.jimage
|
||||
* @build tests.* jdk.test.lib.process.OutputAnalyzer
|
||||
* jdk.test.lib.process.ProcessTools
|
||||
* @run main/othervm -Xmx1g UpgradeableFileCacertsTest
|
||||
*/
|
||||
public class UpgradeableFileCacertsTest extends ModifiedFilesTest {
|
||||
|
||||
/*
|
||||
* Generated with:
|
||||
* $ rm -f server.keystore && keytool -genkey -alias jlink-upgrade-test \
|
||||
* -keyalg RSA -dname CN=jlink-upgrade-test \
|
||||
* -storepass changeit -keysize 3072 -sigalg SHA512withRSA \
|
||||
* -validity 7300 -keystore server.keystore
|
||||
* $ keytool -export -alias jlink-upgrade-test -storepass changeit \
|
||||
* -keystore server.keystore -rfc
|
||||
*/
|
||||
private static final String CERT = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3jCCAkagAwIBAgIJALiT/+HXBkSIMA0GCSqGSIb3DQEBDQUAMB0xGzAZBgNV
|
||||
BAMTEmpsaW5rLXVwZ3JhZGUtdGVzdDAeFw0yNTA0MDQxMjA3MjJaFw00NTAzMzAx
|
||||
MjA3MjJaMB0xGzAZBgNVBAMTEmpsaW5rLXVwZ3JhZGUtdGVzdDCCAaIwDQYJKoZI
|
||||
hvcNAQEBBQADggGPADCCAYoCggGBANmrnCDKqSXEJRIiSi4yHWN97ILls3RqYjED
|
||||
la3AZTeXnZrrEIgSjVFUMxCztYqbWoVzKa2lov42Vue2BXVYffcQ8TKc2EJDNO+2
|
||||
uRKQZpsN7RI4QoVBR2Rq8emrO8CrdOQT7Hh4agxkN9AOvGKMFdt+fXeCIPIuflKP
|
||||
f+RfvhLfC2A70Y+Uu74C5uWgLloA/HF0SsVxf9KmqS9fZBQaiTYhKyoDghCRlWpa
|
||||
nPIHB1XVaRdw8aSpCuzIOQzSCTTlLcammJkBjbFwMZdQG7eglTWzIYryZwe/cyY2
|
||||
xctLVW3xhUHvnMFG+MajeFny2mxNu163Rxf/rBu4e7jRC/LGSU784nJGapq5K170
|
||||
WbaeceKp+YORJBviFFORrmkPIwIgE+iGCD6PD6Xwu8vcpeuTVDgsSWMlfgCL3NoI
|
||||
GXmdGiI2Xc/hQX7uzu3UBF6IcPDMTcYr2JKYbgu3v2/vDlJu3qO2ycUeePo5jhuG
|
||||
X2WgcHkb6uOU4W5qdbCA+wFPVZBuwQIDAQABoyEwHzAdBgNVHQ4EFgQUtMJM0+ct
|
||||
ssKqryRckk4YEWdYAZkwDQYJKoZIhvcNAQENBQADggGBAI8A6gJQ8wDx12sy2ZI4
|
||||
1q9b+WG6w3LcFEF6Fko5NBizhtfmVycQv4mBa/NJgx4DZmd+5d60gJcTp/hJXGY0
|
||||
LZyFilm/AgxsLNUUQLbHAV6TWqd3ODWwswAuew9sFU6izl286a9W65tbMWL5r1EA
|
||||
t34ZYVWZYbCS9+czU98WomH4uarRAOlzcEUui3ZX6ZcQxWbz/R2wtKcUPUAYnsqH
|
||||
JPivpE25G5xW2Dp/yeQTrlffq9OLgZWVz0jtOguBUMnsUsgCcpQZtqZX08//wtpz
|
||||
ohLHFGvpXTPbRumRasWWtnRR/QqGRT66tYDqybXXz37UtKZ8VKW0sv2ypVbmAEs5
|
||||
pLkA/3XiXlstJuCD6cW0Gfbpb5rrPPD46O3FDVlmqlTH3b/MsiQREdydqGzqY7uG
|
||||
AA2GFVaKFASA5ls01CfHLAcrKxSVixditXvsjeIqhddB7Pnbsx20RdzPQoeo9/hF
|
||||
WeIrh4zePDPZChuLR8ZyxeVJhLB71nTrTDDjwXarVez9Xw==
|
||||
-----END CERTIFICATE-----
|
||||
""";
|
||||
|
||||
private static final String CERT_ALIAS = "jlink-upgrade-test";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
UpgradeableFileCacertsTest test = new UpgradeableFileCacertsTest();
|
||||
test.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
String initialImageName() {
|
||||
return "java-base-jlink-upgrade-cacerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
void testAndAssert(Path modifiedFile, Helper helper, Path initialImage) throws Exception {
|
||||
CapturingHandler handler = new CapturingHandler();
|
||||
jlinkUsingImage(new JlinkSpecBuilder()
|
||||
.helper(helper)
|
||||
.imagePath(initialImage)
|
||||
.name("java-base-jlink-upgrade-cacerts-target")
|
||||
.addModule("java.base")
|
||||
.validatingModule("java.base")
|
||||
.build(), handler);
|
||||
OutputAnalyzer analyzer = handler.analyzer();
|
||||
// verify we don't get any modified warning
|
||||
analyzer.stdoutShouldNotContain(modifiedFile.toString() + " has been modified");
|
||||
analyzer.stdoutShouldNotContain("java.lang.IllegalArgumentException");
|
||||
analyzer.stdoutShouldNotContain("IOException");
|
||||
}
|
||||
|
||||
// Add an extra certificate in the cacerts file so that it no longer matches
|
||||
// the recorded hash sum at build time.
|
||||
protected Path modifyFileInImage(Path jmodLessImg)
|
||||
throws IOException, AssertionError {
|
||||
Path cacerts = jmodLessImg.resolve(Path.of("lib", "security", "cacerts"));
|
||||
try (FileInputStream fin = new FileInputStream(cacerts.toFile())) {
|
||||
KeyStore certStore = KeyStore.getInstance(cacerts.toFile(),
|
||||
(char[])null);
|
||||
certStore.load(fin, (char[])null);
|
||||
X509Certificate cert;
|
||||
try (ByteArrayInputStream bin = new ByteArrayInputStream(CERT.getBytes())) {
|
||||
cert = (X509Certificate)generateCertificate(bin);
|
||||
} catch (ClassCastException | CertificateException ce) {
|
||||
throw new AssertionError("Test failed unexpectedly", ce);
|
||||
}
|
||||
certStore.setCertificateEntry(CERT_ALIAS, cert);
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
certStore.store(bout, (char[])null);
|
||||
try (FileOutputStream fout = new FileOutputStream(cacerts.toFile())) {
|
||||
fout.write(bout.toByteArray());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Test failed unexpectedly: ", e);
|
||||
}
|
||||
return cacerts;
|
||||
}
|
||||
|
||||
private Certificate generateCertificate(InputStream in)
|
||||
throws CertificateException, IOException {
|
||||
byte[] data = in.readAllBytes();
|
||||
return CertificateFactory.getInstance("X.509")
|
||||
.generateCertificate(new ByteArrayInputStream(data));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user