Skip to content

Commit bc2f523

Browse files
Applying read-only configuration for Javac internal use of ZIP/JAR file systems.
1 parent 7838321 commit bc2f523

File tree

7 files changed

+87
-29
lines changed

7 files changed

+87
-29
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/file/FSInfo.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,19 @@
2525

2626
package com.sun.tools.javac.file;
2727

28-
import java.io.IOError;
2928
import java.io.IOException;
3029
import java.net.MalformedURLException;
30+
import java.net.URI;
3131
import java.net.URISyntaxException;
3232
import java.net.URL;
33-
import java.nio.file.FileSystems;
3433
import java.nio.file.Files;
35-
import java.nio.file.InvalidPathException;
3634
import java.nio.file.Path;
3735
import java.nio.file.spi.FileSystemProvider;
3836
import java.util.ArrayList;
3937
import java.util.Collections;
38+
import java.util.HashMap;
4039
import java.util.List;
40+
import java.util.Map;
4141
import java.util.StringTokenizer;
4242
import java.util.jar.Attributes;
4343
import java.util.jar.JarFile;
@@ -163,4 +163,30 @@ public synchronized FileSystemProvider getJarFSProvider() {
163163
return null;
164164
}
165165

166+
// Must match the keys/values expected by ZipFileSystem.java.
167+
private static final Map<String, String> READ_ONLY_JARFS_ENV = Map.of(
168+
// Jar files opened by Javac should always be read-only.
169+
"accessMode", "readOnly",
170+
// ignores timestamps not stored in ZIP central directory, reducing I/O.
171+
"zipinfo-time", "false");
172+
173+
/**
174+
* Returns a {@link java.nio.file.FileSystem FileSystem} environment map
175+
* suitable for creating read-only JAR file-systems with default timestamp
176+
* information via {@link FileSystemProvider#newFileSystem(Path, Map)}
177+
* or {@link java.nio.file.FileSystems#newFileSystem(Path, Map)}.
178+
*
179+
* @param releaseVersion the release version to use when creating a
180+
* file-system from a multi-release JAR (or
181+
* {@code null} to ignore release versioning).
182+
*/
183+
public static Map<String, ?> readOnlyJarFSEnv(String releaseVersion) {
184+
if (releaseVersion == null) {
185+
return READ_ONLY_JARFS_ENV;
186+
}
187+
// Multi-release JARs need an additional attribute.
188+
Map<String, String> env = new HashMap<>(READ_ONLY_JARFS_ENV);
189+
env.put("releaseVersion", releaseVersion);
190+
return Collections.unmodifiableMap(env);
191+
}
166192
}

src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -561,13 +561,9 @@ private final class ArchiveContainer implements Container {
561561

562562
public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundException {
563563
this.archivePath = archivePath;
564-
Map<String,String> env = new HashMap<>();
565-
// ignores timestamps not stored in ZIP central directory, reducing I/O
566-
// This key is handled by ZipFileSystem only.
567-
env.put("zipinfo-time", "false");
568-
564+
// Common parameters for opening ZIP/JAR file systems in Javac.
565+
Map<String, ?> env = FSInfo.readOnlyJarFSEnv(multiReleaseValue);
569566
if (multiReleaseValue != null && archivePath.toString().endsWith(".jar")) {
570-
env.put("multi-release", multiReleaseValue);
571567
FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider();
572568
Assert.checkNonNull(jarFSProvider, "should have been caught before!");
573569
try {
@@ -577,8 +573,10 @@ public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundEx
577573
}
578574
} else {
579575
// Less common case is possible if the file manager was not initialized in JavacTask,
580-
// or if non "*.jar" files are on the classpath.
581-
this.fileSystem = FileSystems.newFileSystem(archivePath, env, (ClassLoader)null);
576+
// or if non "*.jar" files are on the classpath. If this is not a ZIP/JAR file then it
577+
// will ignore ZIP specific parameters in env, and may not end up being read-only.
578+
// However Javac should never attempt to write back to archives either way.
579+
this.fileSystem = FileSystems.newFileSystem(archivePath, env);
582580
}
583581
packages = new HashMap<>();
584582
for (Path root : fileSystem.getRootDirectories()) {

src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,10 @@
8181
import com.sun.tools.javac.resources.CompilerProperties.LintWarnings;
8282
import jdk.internal.jmod.JmodFile;
8383

84-
import com.sun.tools.javac.code.Lint;
85-
import com.sun.tools.javac.code.Lint.LintCategory;
8684
import com.sun.tools.javac.main.Option;
8785
import com.sun.tools.javac.resources.CompilerProperties.Errors;
88-
import com.sun.tools.javac.resources.CompilerProperties.Warnings;
8986
import com.sun.tools.javac.util.DefinedBy;
9087
import com.sun.tools.javac.util.DefinedBy.Api;
91-
import com.sun.tools.javac.util.JCDiagnostic.Warning;
9288
import com.sun.tools.javac.util.ListBuffer;
9389
import com.sun.tools.javac.util.Log;
9490
import com.sun.tools.javac.jvm.ModuleNameReader;
@@ -141,7 +137,7 @@ public class Locations {
141137

142138
Map<Path, FileSystem> fileSystems = new LinkedHashMap<>();
143139
List<Closeable> closeables = new ArrayList<>();
144-
private Map<String,String> fsEnv = Collections.emptyMap();
140+
private String releaseVersion = null;
145141

146142
Locations() {
147143
initHandlers();
@@ -233,7 +229,8 @@ private Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault)
233229
}
234230

235231
public void setMultiReleaseValue(String multiReleaseValue) {
236-
fsEnv = Collections.singletonMap("releaseVersion", multiReleaseValue);
232+
// Null is implicitly allowed and unsets the value.
233+
this.releaseVersion = multiReleaseValue;
237234
}
238235

239236
private boolean contains(Collection<Path> searchPath, Path file) throws IOException {
@@ -480,7 +477,7 @@ Location getLocationForModule(String moduleName) throws IOException {
480477
}
481478

482479
/**
483-
* @see JavaFileManager#getLocationForModule(Location, JavaFileObject, String)
480+
* @see JavaFileManager#getLocationForModule(Location, JavaFileObject)
484481
*/
485482
Location getLocationForModule(Path file) throws IOException {
486483
return null;
@@ -1387,7 +1384,7 @@ private Pair<String,Path> inferModuleName(Path p) {
13871384
log.error(Errors.NoZipfsForArchive(p));
13881385
return null;
13891386
}
1390-
try (FileSystem fs = jarFSProvider.newFileSystem(p, fsEnv)) {
1387+
try (FileSystem fs = jarFSProvider.newFileSystem(p, FSInfo.readOnlyJarFSEnv(releaseVersion))) {
13911388
Path moduleInfoClass = fs.getPath("module-info.class");
13921389
if (Files.exists(moduleInfoClass)) {
13931390
String moduleName = readModuleName(moduleInfoClass);
@@ -1463,7 +1460,7 @@ private Pair<String,Path> inferModuleName(Path p) {
14631460
log.error(Errors.LocnCantReadFile(p));
14641461
return null;
14651462
}
1466-
fs = jarFSProvider.newFileSystem(p, Collections.emptyMap());
1463+
fs = jarFSProvider.newFileSystem(p, FSInfo.readOnlyJarFSEnv(null));
14671464
try {
14681465
Path moduleInfoClass = fs.getPath("classes/module-info.class");
14691466
String moduleName = readModuleName(moduleInfoClass);

src/jdk.compiler/share/classes/com/sun/tools/javac/platform/JDKPlatformProvider.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,8 @@
5151
import javax.annotation.processing.Processor;
5252
import javax.tools.ForwardingJavaFileObject;
5353
import javax.tools.JavaFileManager;
54-
import javax.tools.JavaFileManager.Location;
5554
import javax.tools.JavaFileObject;
5655
import javax.tools.JavaFileObject.Kind;
57-
import javax.tools.StandardJavaFileManager;
5856
import javax.tools.StandardLocation;
5957

6058
import com.sun.source.util.Plugin;
@@ -96,6 +94,14 @@ public PlatformDescription getPlatformTrusted(String platformName) {
9694

9795
private static final String[] symbolFileLocation = { "lib", "ct.sym" };
9896

97+
// These must match attributes defined in ZipFileSystem.java.
98+
private static final Map<String, ?> CT_SYM_ZIP_ENV = Map.of(
99+
// Symbol file should always be opened read-only.
100+
"accessMode", "readOnly",
101+
// Uses less accurate, but faster, timestamp information
102+
// (nobody should care about timestamps in the CT symbol file).
103+
"zipinfo-time", "false");
104+
99105
private static final Set<String> SUPPORTED_JAVA_PLATFORM_VERSIONS;
100106
public static final Comparator<String> NUMERICAL_COMPARATOR = (s1, s2) -> {
101107
int i1;
@@ -117,7 +123,7 @@ public PlatformDescription getPlatformTrusted(String platformName) {
117123
SUPPORTED_JAVA_PLATFORM_VERSIONS = new TreeSet<>(NUMERICAL_COMPARATOR);
118124
Path ctSymFile = findCtSym();
119125
if (Files.exists(ctSymFile)) {
120-
try (FileSystem fs = FileSystems.newFileSystem(ctSymFile, (ClassLoader)null);
126+
try (FileSystem fs = FileSystems.newFileSystem(ctSymFile, CT_SYM_ZIP_ENV);
121127
DirectoryStream<Path> dir =
122128
Files.newDirectoryStream(fs.getRootDirectories().iterator().next())) {
123129
for (Path section : dir) {
@@ -249,7 +255,11 @@ public String inferBinaryName(Location location, JavaFileObject file) {
249255
try {
250256
FileSystem fs = ctSym2FileSystem.get(file);
251257
if (fs == null) {
252-
ctSym2FileSystem.put(file, fs = FileSystems.newFileSystem(file, (ClassLoader)null));
258+
fs = FileSystems.newFileSystem(file, CT_SYM_ZIP_ENV);
259+
// If for any reason this was not opened from a ZIP file,
260+
// then the resulting file system would not be read-only.
261+
assert fs.isReadOnly();
262+
ctSym2FileSystem.put(file, fs);
253263
}
254264

255265
Path root = fs.getRootDirectories().iterator().next();

test/langtools/tools/javac/api/file/SJFM_TestBase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.ArrayList;
3838
import java.util.Arrays;
3939
import java.util.List;
40+
import java.util.Map;
4041
import java.util.stream.Collectors;
4142
import java.util.zip.ZipEntry;
4243
import java.util.zip.ZipOutputStream;
@@ -160,7 +161,7 @@ List<Path> getTestFilePaths() throws IOException {
160161
List<Path> getTestZipPaths() throws IOException {
161162
if (zipfs == null) {
162163
Path testZip = createSourceZip();
163-
zipfs = FileSystems.newFileSystem(testZip);
164+
zipfs = FileSystems.newFileSystem(testZip, Map.of("accessMode", "readOnly"));
164165
closeables.add(zipfs);
165166
zipPaths = Files.list(zipfs.getRootDirectories().iterator().next())
166167
.filter(p -> p.getFileName().toString().endsWith(".java"))

test/langtools/tools/javac/jvm/ClassRefDupInConstantPoolTest.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,37 @@
2525
* @test
2626
* @bug 8015927
2727
* @summary Class reference duplicates in constant pool
28-
* @clean ClassRefDupInConstantPoolTest$Duplicates
28+
* @library /tools/lib
29+
* @modules jdk.compiler/com.sun.tools.javac.api
30+
* jdk.compiler/com.sun.tools.javac.main
31+
* @build toolbox.JavacTask toolbox.ToolBox
2932
* @run main ClassRefDupInConstantPoolTest
3033
*/
3134

35+
import java.nio.file.Files;
36+
import java.nio.file.Path;
3237
import java.util.TreeSet;
3338

39+
import toolbox.JavacTask;
40+
import toolbox.ToolBox;
41+
3442
import java.lang.classfile.*;
3543
import java.lang.classfile.constantpool.*;
3644

3745
public class ClassRefDupInConstantPoolTest {
46+
47+
private static final String DUPLICATE_REFS_CLASS =
48+
"""
49+
class Duplicates {
50+
String concat(String s1, String s2) {
51+
return s1 + (s2 == s1 ? " " : s2);
52+
}
53+
}""";
54+
3855
public static void main(String[] args) throws Exception {
39-
ClassModel cls = ClassFile.of().parse(ClassRefDupInConstantPoolTest.class.
40-
getResourceAsStream("ClassRefDupInConstantPoolTest$Duplicates.class").readAllBytes());
56+
new JavacTask(new ToolBox()).sources(DUPLICATE_REFS_CLASS).run();
57+
58+
ClassModel cls = ClassFile.of().parse(Files.readAllBytes(Path.of("Duplicates.class")));
4159
ConstantPool pool = cls.constantPool();
4260

4361
int duplicates = 0;

test/langtools/tools/javac/platform/VerifyCTSymClassFiles.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@
2727
* @summary Verify classfile inside ct.sym
2828
* @library /tools/lib
2929
* @modules jdk.compiler/com.sun.tools.javac.api
30+
* jdk.compiler/com.sun.tools.javac.file
3031
* jdk.compiler/com.sun.tools.javac.main
3132
* jdk.compiler/com.sun.tools.javac.platform
3233
* jdk.compiler/com.sun.tools.javac.util:+open
3334
* @build toolbox.ToolBox VerifyCTSymClassFiles
3435
* @run main VerifyCTSymClassFiles
3536
*/
3637

38+
import com.sun.tools.javac.file.FSInfo;
39+
3740
import java.io.IOException;
3841
import java.io.UncheckedIOException;
3942
import java.lang.classfile.ClassFile;
@@ -60,7 +63,12 @@ void checkClassFiles() throws IOException {
6063
//no ct.sym, nothing to check:
6164
return ;
6265
}
63-
try (FileSystem fs = FileSystems.newFileSystem(ctSym)) {
66+
// Expected to always be a ZIP filesystem.
67+
try (FileSystem fs = FileSystems.newFileSystem(ctSym, FSInfo.readOnlyJarFSEnv(null))) {
68+
// Check that the file system is read only (not true if not a zip file system).
69+
if (!fs.isReadOnly()) {
70+
throw new AssertionError("Expected read-only file system");
71+
}
6472
Files.walk(fs.getRootDirectories().iterator().next())
6573
.filter(p -> Files.isRegularFile(p))
6674
.forEach(p -> checkClassFile(p));

0 commit comments

Comments
 (0)