Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NativeImageResourceFileSystem: Path.toURI produces wrong URI; Files.isSameFile throws exception #5720

Closed
kohlschuetter opened this issue Jan 7, 2023 · 2 comments

Comments

@kohlschuetter
Copy link

When using classpath resources in a native-image binary, the native image resource filesystem behaves erratically, preventing its use (without workarounds) where Path URIs are being used, such as Jetty 12.

The URI produced from a native-image resource path by calling Path#toURI is not equivalent to the URI returned by Class#getResource(String).toURI:

The original URI is of the form resource:/package/subpackage/file.
Creating a Path from it (Path.of(URI)) yields a toString/toRealPath string of /package/subpackage/file and a file system of class com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem.
Calling toURI on the path results in a URI of the form resource:file:///resources!/package/subpackage/file.
Creating a Path from that URI results in a in a path string of file:/resources!/package/subpackage/file, which doesn't exist as per Files.exists.

Moreover, trying to compare these paths using Files.isSameFile results in a java.nio.file.NoSuchFileException being thrown:

java.nio.file.NoSuchFileException: /resources
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem.checkAccess(NativeImageResourceFileSystem.java:394)
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.resources.NativeImageResourcePath.checkAccess(NativeImageResourcePath.java:728)
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.resources.NativeImageResourcePath.isSameFile(NativeImageResourcePath.java:709)
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider.isSameFile(NativeImageResourceFileSystemProvider.java:194)
	at [email protected]/java.nio.file.Files.isSameFile(Files.java:1539)
	at ...

Steps to reproduce:

  1. Have a main class (e.g., TestClass) in some package (e.g., package.subpackage) and a corresponding resource in the class path under /package/subpackage/file. The code below is to be run from the main method.
  2. Get the resource URI via URI uri = TestClass.class.getResource("file").toURI()
  3. Obtain the resource Path via
Path path;
try {
  path = Path.of(uri);
} catch (FileSystemNotFoundException e) {
  FileSystems.newFileSystem(uri, Collections.emptyMap()); // required for ZipFileSystem, for example.
  path = Path.of(uri);
}
  1. Convert the path to URI again
URI uri2 = path.toURI();
Path path2 = Path.of(uri2);
  1. Print uris and paths:
System.out.println(uri);
System.out.println(path);
System.out.println(uri2);
System.out.println(path2);
  1. Compare paths:
System.out.println(path.equals(path2)); // only not `true` with native-image
System.out.println(Files.isSameFile(path, path2)); // only throws java.nio.file.NoSuchFileException with native-image

Compare the output between a Hotspot build (e.g., from a jar file (which uses ZipFileSystem), from a default file system via Eclipse, and a native-image build).

Version info: 'GraalVM 22.3.0 Java 19 CE'
Java version info: '19.0.1+10-jvmci-22.3-b08'
C compiler: cc (apple, arm64, 14.0.0)
Garbage collector: Serial GC

kohlschuetter added a commit to kohlschuetter/jetty.project that referenced this issue Jan 7, 2023
GraalVM Native-Image makes its classpath resources accessible through an
opaque resource: URL scheme.

Add support for this scheme in URIUtil and PathResource.

Automatically create the NativeImageResourceFileSystem when necessary.
Also add a workaround where converting such Native-Image Paths back to
URI would yield the wrong URI (potentially a bug in GraalVM).

jetty#9116
oracle/graal#5720
kohlschuetter added a commit to kohlschuetter/jetty.project that referenced this issue Jan 7, 2023
GraalVM Native-Image makes its classpath resources accessible through an
opaque resource: URL scheme.

Add support for this scheme in URIUtil and PathResource.

Automatically create the NativeImageResourceFileSystem when necessary.
Also add a workaround where converting such Native-Image Paths back to
URI would yield the wrong URI (potentially a bug in GraalVM).

jetty#9116
oracle/graal#5720
kohlschuetter added a commit to kohlschuetter/jetty.project that referenced this issue Jan 7, 2023
GraalVM Native-Image makes its classpath resources accessible through an
opaque resource: URL scheme.

Add support for this scheme in URIUtil and PathResource.

Automatically create the NativeImageResourceFileSystem when necessary.
Also add a workaround where converting such Native-Image Paths back to
URI would yield the wrong URI (potentially a bug in GraalVM).

jetty#9116
oracle/graal#5720

Signed-off-by: Christian Kohlschütter <[email protected]>
kohlschuetter added a commit to kohlschuetter/jetty.project that referenced this issue Jan 7, 2023
GraalVM Native-Image makes its classpath resources accessible through an
opaque resource: URL scheme.

Add support for this scheme in URIUtil and PathResource.

Automatically create the NativeImageResourceFileSystem when necessary.
Also add a workaround where converting such Native-Image Paths back to
URI would yield the wrong URI (potentially a bug in GraalVM).

jetty#9116
oracle/graal#5720

Signed-off-by: Christian Kohlschütter <[email protected]>
@jovanstevanovic
Copy link
Member

Internally tracked as GR-43605.

joakime added a commit to jetty/jetty.project that referenced this issue Jan 17, 2023
…rcePath2

* util: Add support for GraalVM Native-Image resource:-URIs and Paths

GraalVM Native-Image makes its classpath resources accessible through an
opaque resource: URL scheme.

Add support for this scheme in URIUtil and PathResource.

Automatically create the NativeImageResourceFileSystem when necessary.
Also add a workaround where converting such Native-Image Paths back to
URI would yield the wrong URI (potentially a bug in GraalVM).

#9116
oracle/graal#5720

* URIUtil: Suppress CodeQL false positive error about path injection

Github CodeQL code scanning reports a high-severity error "Uncontrolled
data used in path expression", because a path depends on a user-provided
value.

This is a false positive.

Suppress the error by annotating a corresponding @SuppressWarnings tag.

Signed-off-by: Christian Kohlschütter <[email protected]>

* URIUtil: Removed unused code

KNOWN_SCHEMES isn't used anywhere.

* PathResource: Selectively enable resource: for GraalVM Native Image

In regular HotSpot VMs, the resource: scheme may be registered by other
clients. However, in GraalVM Native Image, this is used for classpath
resources.

This resource scheme needs to be enabled by default for Native Image
environments so we can support code that is unaware of GraalVM
internals, such as:

URL resourceURL = MyClass.class.getResource("some/resource");
Resource resource = ResourceFactory.root().newResource(resourceURL);

Signed-off-by: Christian Kohlschütter <[email protected]>

* PathResourceFactory: Ignore certain RuntimeExceptions upon init

Ignore FileSystemAlreadyExistsException, ProviderNotFoundException, etc.
that may be thrown upon calling FileSystems.newFileSystem.

Signed-off-by: Christian Kohlschütter <[email protected]>

* PathResource: Work-around false positive CodeQL warning

CodeQL prevents amending the call to Paths.get.

Work-around this by using a separate constructor. The additional benefit
is that URIUtil.correctResourceURI won't need to correct the URI twice.

Signed-off-by: Christian Kohlschütter <[email protected]>

* PathResource: Fix "isAlias" issue with resource: URIs

Fix two more places where Native Image resource: URIs need to be
changed.

Without this change, Resource#isAlias() would return true for such URIs,
and a warning like "BaseResource resource:/some/package/ is aliased to
resource:file:///resources!/some/package/" would be logged.

* Move GraalVM Native-Image code to NativeImagePathResource/-Factory

Keep the GraalVM Native-Image code contained in custom subclasses.

We still enable the "resource:" URL scheme by default if a GraalVM
Native-Image environment is detected via System property. This allows us
to transparently support calls like
ResourceFactory.root().newResource(MyClass.class.getResorce("webapp"))

Signed-off-by: Christian Kohlschütter <[email protected]>

* Refactor Graal native-image resource: handling

Detect the resource: scheme by checking the scheme of a well-known
resource instead of looking at a system property.

Rename NativeImagePathResource* to GraalIssue5720PathResource* and make
these classes package-private.

Signed-off-by: Christian Kohlschütter <[email protected]>

* Only use GraalIssue5720PathResourceFactory when truly needed

Previously, we were always enabling this resource factory for GraalVM
native-image environments.

We now check if that's actually necessary.

We fall back to MountedPathResourceFactory or PathResourceFactory,
depending on whether the URL contains "!/" or not.

Signed-off-by: Christian Kohlschütter <[email protected]>
@jovanstevanovic
Copy link
Member

The fix is on the master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants