diff --git a/src/share/classes/sun/misc/URLClassPath.java b/src/share/classes/sun/misc/URLClassPath.java index 5f56056bcb5383ce09cf28a5cae9f4af5c00c79a..e47d3f97cd299d9a838fcd4e6035f54902decc8e 100644 --- a/src/share/classes/sun/misc/URLClassPath.java +++ b/src/share/classes/sun/misc/URLClassPath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2018, 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 @@ -37,6 +37,7 @@ import java.util.jar.Attributes; import java.util.jar.Attributes.Name; import java.net.JarURLConnection; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; @@ -69,6 +70,8 @@ public class URLClassPath { private static final boolean DEBUG_LOOKUP_CACHE; private static final boolean DISABLE_JAR_CHECKING; private static final boolean DISABLE_ACC_CHECKING; + private static final boolean DISABLE_CP_URL_CHECK; + private static final boolean DEBUG_CP_URL_CHECK; static { JAVA_VERSION = java.security.AccessController.doPrivileged( @@ -84,6 +87,13 @@ public class URLClassPath { p = AccessController.doPrivileged( new GetPropertyAction("jdk.net.URLClassPath.disableRestrictedPermissions")); DISABLE_ACC_CHECKING = p != null ? p.equals("true") || p.equals("") : false; + + // This property will be removed in a later release + p = AccessController.doPrivileged( + new GetPropertyAction("jdk.net.URLClassPath.disableClassPathURLCheck")); + + DISABLE_CP_URL_CHECK = p != null ? p.equals("true") || p.isEmpty() : false; + DEBUG_CP_URL_CHECK = "debug".equals(p); } /* The original search path of URLs. */ @@ -1217,11 +1227,51 @@ public class URLClassPath { int i = 0; while (st.hasMoreTokens()) { String path = st.nextToken(); - urls[i] = new URL(base, path); - i++; + URL url = DISABLE_CP_URL_CHECK ? new URL(base, path) : safeResolve(base, path); + if (url != null) { + urls[i] = url; + i++; + } + } + if (i == 0) { + urls = null; + } else if (i != urls.length) { + // Truncate nulls from end of array + urls = Arrays.copyOf(urls, i); } return urls; } + + /* + * Return a URL for the given path resolved against the base URL, or + * null if the resulting URL is invalid. + */ + static URL safeResolve(URL base, String path) { + String child = path.replace(File.separatorChar, '/'); + try { + if (!URI.create(child).isAbsolute()) { + URL url = new URL(base, child); + if (base.getProtocol().equalsIgnoreCase("file")) { + return url; + } else { + String bp = base.getPath(); + String urlp = url.getPath(); + int pos = bp.lastIndexOf('/'); + if (pos == -1) { + pos = bp.length() - 1; + } + if (urlp.regionMatches(0, bp, 0, pos + 1) + && urlp.indexOf("..", pos) == -1) { + return url; + } + } + } + } catch (MalformedURLException | IllegalArgumentException e) {} + if (DEBUG_CP_URL_CHECK) { + System.err.println("Class-Path entry: \"" + path + "\" ignored in JAR file " + base); + } + return null; + } } /* diff --git a/test/lib/testlibrary/jdk/testlibrary/JarUtils.java b/test/lib/testlibrary/jdk/testlibrary/JarUtils.java index b8e9970c91384f9c49f983ad9ca70ae6bb198730..8d1e9ef6e57e61347e83a160df513cf769c40f69 100644 --- a/test/lib/testlibrary/jdk/testlibrary/JarUtils.java +++ b/test/lib/testlibrary/jdk/testlibrary/JarUtils.java @@ -24,16 +24,20 @@ package jdk.testlibrary; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -45,6 +49,72 @@ import java.util.jar.Manifest; */ public final class JarUtils { + /** + * Creates a JAR file. + * + * Equivalent to {@code jar cfm -C file...} + * + * The input files are resolved against the given directory. Any input + * files that are directories are processed recursively. + */ + public static void createJarFile(Path jarfile, Manifest man, Path dir, Path... file) + throws IOException + { + // create the target directory + Path parent = jarfile.getParent(); + if (parent != null) + Files.createDirectories(parent); + + List entries = new ArrayList<>(); + for (Path entry : file) { + Files.find(dir.resolve(entry), Integer.MAX_VALUE, + (p, attrs) -> attrs.isRegularFile()) + .map(e -> dir.relativize(e)) + .forEach(entries::add); + } + + try (OutputStream out = Files.newOutputStream(jarfile); + JarOutputStream jos = new JarOutputStream(out)) + { + if (man != null) { + JarEntry je = new JarEntry(JarFile.MANIFEST_NAME); + jos.putNextEntry(je); + man.write(jos); + jos.closeEntry(); + } + + for (Path entry : entries) { + String name = toJarEntryName(entry); + jos.putNextEntry(new JarEntry(name)); + Files.copy(dir.resolve(entry), jos); + jos.closeEntry(); + } + } + } + + /** + * Creates a JAR file. + * + * Equivalent to {@code jar cf -C file...} + * + * The input files are resolved against the given directory. Any input + * files that are directories are processed recursively. + */ + public static void createJarFile(Path jarfile, Path dir, Path... file) + throws IOException + { + createJarFile(jarfile, null, dir, file); + } + + /** + * Creates a JAR file from the contents of a directory. + * + * Equivalent to {@code jar cf -C .} + */ + public static void createJarFile(Path jarfile, Path dir) throws IOException { + createJarFile(jarfile, dir, Paths.get(".")); + } + /** * Create jar file with specified files. If a specified file does not exist, * a new jar entry will be created with the file name itself as the content. @@ -196,4 +266,14 @@ public final class JarUtils { } } } + + /** + * Map a file path to the equivalent name in a JAR file + */ + private static String toJarEntryName(Path file) { + Path normalized = file.normalize(); + return normalized.subpath(0, normalized.getNameCount()) // drop root + .toString() + .replace(File.separatorChar, '/'); + } }