提交 55efe603 编写于 作者: I igerasim

8195874: Improve jar specification adherence

Summary: Also reviewed by Chris Ries <chris.ries@oracle.com>
Reviewed-by: alanb, mchung, rriggs
上级 504c4e7f
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -37,6 +37,7 @@ import java.util.jar.Attributes; ...@@ -37,6 +37,7 @@ import java.util.jar.Attributes;
import java.util.jar.Attributes.Name; import java.util.jar.Attributes.Name;
import java.net.JarURLConnection; import java.net.JarURLConnection;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.net.URLConnection; import java.net.URLConnection;
...@@ -69,6 +70,8 @@ public class URLClassPath { ...@@ -69,6 +70,8 @@ public class URLClassPath {
private static final boolean DEBUG_LOOKUP_CACHE; private static final boolean DEBUG_LOOKUP_CACHE;
private static final boolean DISABLE_JAR_CHECKING; private static final boolean DISABLE_JAR_CHECKING;
private static final boolean DISABLE_ACC_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 { static {
JAVA_VERSION = java.security.AccessController.doPrivileged( JAVA_VERSION = java.security.AccessController.doPrivileged(
...@@ -84,6 +87,13 @@ public class URLClassPath { ...@@ -84,6 +87,13 @@ public class URLClassPath {
p = AccessController.doPrivileged( p = AccessController.doPrivileged(
new GetPropertyAction("jdk.net.URLClassPath.disableRestrictedPermissions")); new GetPropertyAction("jdk.net.URLClassPath.disableRestrictedPermissions"));
DISABLE_ACC_CHECKING = p != null ? p.equals("true") || p.equals("") : false; 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. */ /* The original search path of URLs. */
...@@ -1217,11 +1227,51 @@ public class URLClassPath { ...@@ -1217,11 +1227,51 @@ public class URLClassPath {
int i = 0; int i = 0;
while (st.hasMoreTokens()) { while (st.hasMoreTokens()) {
String path = st.nextToken(); String path = st.nextToken();
urls[i] = new URL(base, path); URL url = DISABLE_CP_URL_CHECK ? new URL(base, path) : safeResolve(base, path);
i++; 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 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;
}
} }
/* /*
......
...@@ -24,16 +24,20 @@ ...@@ -24,16 +24,20 @@
package jdk.testlibrary; package jdk.testlibrary;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
...@@ -45,6 +49,72 @@ import java.util.jar.Manifest; ...@@ -45,6 +49,72 @@ import java.util.jar.Manifest;
*/ */
public final class JarUtils { public final class JarUtils {
/**
* Creates a JAR file.
*
* Equivalent to {@code jar cfm <jarfile> <manifest> -C <dir> 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<Path> 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 <jarfile> -C <dir> 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 <jarfile> -C <dir> .}
*/
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, * 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. * a new jar entry will be created with the file name itself as the content.
...@@ -196,4 +266,14 @@ public final class JarUtils { ...@@ -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, '/');
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册