From 7c810c82f05efe198b9fd3143b2aeedc5831e3cf Mon Sep 17 00:00:00 2001 From: jovanstevanovic Date: Fri, 16 Jul 2021 13:38:38 +0200 Subject: [PATCH] Added support to register/access resource with trailing slash. --- .../com/oracle/svm/core/jdk/Resources.java | 29 +++++++++++++++---- .../resources/NativeImageResourcePath.java | 4 +-- .../jdk/resources/ResourceURLConnection.java | 2 +- .../oracle/svm/hosted/ResourcesFeature.java | 13 ++++++--- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java index 82c9f433d685..7a365e2666c0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java @@ -31,13 +31,12 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; -import com.oracle.svm.core.jdk.resources.ResourceStorageEntry; -import com.oracle.svm.core.jdk.resources.ResourceURLConnection; import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -45,6 +44,9 @@ import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.jdk.resources.NativeImageResourcePath; +import com.oracle.svm.core.jdk.resources.ResourceStorageEntry; +import com.oracle.svm.core.jdk.resources.ResourceURLConnection; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; @@ -57,6 +59,8 @@ */ public final class Resources { + public static final char RESOURCES_INTERNAL_PATH_SEPARATOR = '/'; + public static Resources singleton() { return ImageSingletons.lookup(Resources.class); } @@ -121,6 +125,15 @@ public static void registerDirectoryResource(String resourceDirName, String cont addEntry(resourceDirName, true, content.getBytes()); } + /** + * Avoid pulling native file system by using {@link NativeImageResourcePath} implementation to + * convert resourceName to canonical variant. + */ + public static String toCanonicalForm(String resourceName) { + NativeImageResourcePath path = new NativeImageResourcePath(null, resourceName.getBytes(StandardCharsets.UTF_8), true); + return new String(NativeImageResourcePath.getResolved(path)); + } + public static ResourceStorageEntry get(String name) { return singleton().resources.get(name); } @@ -144,7 +157,8 @@ public static URL createURL(String resourceName) { if (resourceName == null) { return null; } - Enumeration urls = createURLs(resourceName); + + Enumeration urls = createURLs(toCanonicalForm(resourceName)); return urls.hasMoreElements() ? urls.nextElement() : null; } @@ -153,7 +167,8 @@ public static InputStream createInputStream(String resourceName) { if (resourceName == null) { return null; } - ResourceStorageEntry entry = Resources.get(resourceName); + + ResourceStorageEntry entry = Resources.get(toCanonicalForm(resourceName)); if (entry == null) { return null; } @@ -165,14 +180,16 @@ public static Enumeration createURLs(String resourceName) { if (resourceName == null) { return null; } - ResourceStorageEntry entry = Resources.get(resourceName); + + String canonicalResourceName = toCanonicalForm(resourceName); + ResourceStorageEntry entry = Resources.get(canonicalResourceName); if (entry == null) { return Collections.emptyEnumeration(); } int numberOfResources = entry.getData().size(); List resourcesURLs = new ArrayList<>(numberOfResources); for (int index = 0; index < numberOfResources; index++) { - resourcesURLs.add(createURL(resourceName, index)); + resourcesURLs.add(createURL(canonicalResourceName, index)); } return Collections.enumeration(resourcesURLs); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourcePath.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourcePath.java index 3c7bccdd5bac..f3a3b4b4a0de 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourcePath.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourcePath.java @@ -603,13 +603,13 @@ private byte[] getResolved() { } for (byte c : path) { if (c == '.') { - return doGetResolved(this); + return getResolved(this); } } return path; } - private static byte[] doGetResolved(NativeImageResourcePath p) { + public static byte[] getResolved(NativeImageResourcePath p) { int nc = p.getNameCount(); byte[] path = p.path; int[] offsets = p.offsets; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java index 3aa271c5f30d..8733cd70f25c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java @@ -63,7 +63,7 @@ public void connect() { connected = true; String resourceName = resolveName(url.getPath()); - ResourceStorageEntry entry = Resources.get(resourceName); + ResourceStorageEntry entry = Resources.get(Resources.toCanonicalForm(resourceName)); if (entry != null) { List bytes = entry.getData(); if (index < bytes.size()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 606dbf6322a4..fbdaa447b084 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -25,6 +25,8 @@ package com.oracle.svm.hosted; +import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -48,6 +50,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.svm.core.util.VMError; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionType; @@ -268,7 +271,7 @@ private static void scanDirectory(DebugContext debugContext, Path root, Pattern[ /* Resources always use / as the separator, as do our resource inclusion patterns */ String relativeFilePath; if (entry != root) { - relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, '/'); + relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); allEntries.add(relativeFilePath); } else { relativeFilePath = ""; @@ -291,7 +294,7 @@ private static void scanDirectory(DebugContext debugContext, Path root, Pattern[ } for (String entry : allEntries) { - int last = entry.lastIndexOf('/'); + int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); String key = last == -1 ? "" : entry.substring(0, last); List dirContent = matchedDirectoryResources.get(key); if (dirContent != null && !dirContent.contains(entry)) { @@ -328,14 +331,16 @@ private static void scanJar(DebugContext debugContext, Path jarPath, Pattern[] i } private static boolean matches(Pattern[] includePatterns, Pattern[] excludePatterns, String relativePath) { + VMError.guarantee(!relativePath.contains("\\"), "Resource path contains backslash!"); + String relativePathWithTrailingSlash = relativePath + RESOURCES_INTERNAL_PATH_SEPARATOR; for (Pattern p : excludePatterns) { - if (p.matcher(relativePath).matches()) { + if (p.matcher(relativePath).matches() || p.matcher(relativePathWithTrailingSlash).matches()) { return false; } } for (Pattern p : includePatterns) { - if (p.matcher(relativePath).matches()) { + if (p.matcher(relativePath).matches() || p.matcher(relativePathWithTrailingSlash).matches()) { return true; } }