src/share/classes/org/openjdk/jigsaw/ModuleFile.java

Print this page

        

*** 31,40 **** --- 31,41 ---- import java.util.*; import java.util.jar.*; import java.util.zip.*; import static org.openjdk.jigsaw.FileConstants.ModuleFile.*; + import static org.openjdk.jigsaw.FileConstants.ModuleFile.SectionType.*; public final class ModuleFile { /** * Return the subdir of a section in an extracted module file. */
*** 66,76 **** private File natlibs; private File natcmds; private File configs; private static class CountingInputStream extends FilterInputStream { ! int count; public CountingInputStream(InputStream stream, int count) { super(stream); this.count = count; } --- 67,77 ---- private File natlibs; private File natcmds; private File configs; private static class CountingInputStream extends FilterInputStream { ! private int count; public CountingInputStream(InputStream stream, int count) { super(stream); this.count = count; }
*** 112,122 **** --- 113,128 ---- long skipped = super.skip(n); if (n > 0) count-=skipped; return skipped; } + + public void close() throws IOException { + // Do nothing, CountingInputStream is used to wrap (sub)section + // content. We never want to close the underlying stream. } + } public Reader(DataInputStream stream) { hashtype = HashType.SHA256; // Ensure that mark/reset is supported if (stream.markSupported()) {
*** 125,144 **** this.stream = new DataInputStream(new BufferedInputStream(stream)); } } - private void checkHashMatch(byte[] expected, byte[] computed) - throws IOException - { - if (!MessageDigest.isEqual(expected, computed)) - throw new IOException("Expected hash " - + hashHexString(expected) - + " instead of " - + hashHexString(computed)); - } - private ModuleFileHeader fileHeader = null; private MessageDigest fileDigest = null; private MessageDigest sectionDigest = null; private DataInputStream fileIn = null; private byte[] moduleInfoBytes = null; --- 131,140 ----
*** 145,154 **** --- 141,151 ---- private Integer moduleSignatureType = null; private byte[] moduleSignatureBytes = null; private final int MAX_SECTION_HEADER_LENGTH = 128; private List<byte[]> calculatedHashes = new ArrayList<>(); private boolean extract = true; + private List<String> contents; // list of the module-file contents /* * Reads the MODULE_INFO section and the Signature section, if present, * but does not write any files. */
*** 165,175 **** fileHeader.write(new DataOutputStream(baos)); sectionDigest.update(baos.toByteArray()); calculatedHashes.add(sectionDigest.digest()); fileIn = new DataInputStream(dis); ! if (readSection(fileIn) != SectionType.MODULE_INFO) throw new IOException("First module-file section" + " is not MODULE_INFO"); assert moduleInfoBytes != null; // Read the Signature Section, if present --- 162,172 ---- fileHeader.write(new DataOutputStream(baos)); sectionDigest.update(baos.toByteArray()); calculatedHashes.add(sectionDigest.digest()); fileIn = new DataInputStream(dis); ! if (readSection(fileIn) != MODULE_INFO) throw new IOException("First module-file section" + " is not MODULE_INFO"); assert moduleInfoBytes != null; // Read the Signature Section, if present
*** 198,215 **** this.deflate = deflate; this.destination = dst != null ? dst.getCanonicalFile() : null; this.natlibs = natlibs != null ? natlibs : new File(destination, "lib"); this.natcmds = natcmds != null ? natcmds : new File(destination, "bin"); this.configs = configs != null ? configs : new File(destination, "etc"); try { if (extract) Files.store(moduleInfoBytes, computeRealPath("info")); // Module-Info and Signature, if present, have been consumed // Read rest of file until all sections have been read stream.mark(1); ! while (-1 != stream.read()) { stream.reset(); readSection(fileIn); stream.mark(1); } --- 195,215 ---- this.deflate = deflate; this.destination = dst != null ? dst.getCanonicalFile() : null; this.natlibs = natlibs != null ? natlibs : new File(destination, "lib"); this.natcmds = natcmds != null ? natcmds : new File(destination, "bin"); this.configs = configs != null ? configs : new File(destination, "etc"); + contents = new ArrayList<>(); + try { if (extract) Files.store(moduleInfoBytes, computeRealPath("info")); + contents.add("module-info.class"); // Module-Info and Signature, if present, have been consumed // Read rest of file until all sections have been read stream.mark(1); ! while (stream.read() != -1) { stream.reset(); readSection(fileIn); stream.mark(1); }
*** 254,278 **** byte[] getSignatureNoClone() { return moduleSignatureBytes; } ! private JarOutputStream contentStream = null; ! ! private JarOutputStream contentStream() throws IOException { ! if (contentStream == null) { ! if (extract) { ! FileOutputStream fos ! = new FileOutputStream(computeRealPath("classes")); ! contentStream ! = new JarOutputStream(new BufferedOutputStream(fos)); ! } else { ! contentStream = new JarOutputStream(new NullOutputStream()); } - } - return contentStream; - } public void close() throws IOException { try { try { if (contentStream != null) { --- 254,269 ---- byte[] getSignatureNoClone() { return moduleSignatureBytes; } ! public List<String> getContents() throws IOException { ! if (contents == null) ! readModule(); ! Collections.sort(contents); ! return contents; } public void close() throws IOException { try { try { if (contentStream != null) {
*** 306,332 **** private void readSignatureSection(DataInputStream stream, DigestInputStream dis) throws IOException { - // Turn off digest computation before reading Signature Section dis.on(false); // Mark the starting position stream.mark(MAX_SECTION_HEADER_LENGTH); if (stream.read() != -1) { stream.reset(); SectionHeader header = SectionHeader.read(stream); ! if (header != null && ! header.getType() == SectionType.SIGNATURE) { readSectionContent(header, stream); ! } else { ! // Revert back to the starting position ! stream.reset(); } - } // Turn on digest computation again dis.on(true); } --- 297,319 ---- private void readSignatureSection(DataInputStream stream, DigestInputStream dis) throws IOException { // Turn off digest computation before reading Signature Section dis.on(false); // Mark the starting position stream.mark(MAX_SECTION_HEADER_LENGTH); if (stream.read() != -1) { stream.reset(); SectionHeader header = SectionHeader.read(stream); ! if (header != null && header.getType() == SIGNATURE) readSectionContent(header, stream); ! else ! stream.reset(); // No signature, reset back to start } // Turn on digest computation again dis.on(true); }
*** 343,422 **** throws IOException { SectionType type = header.getType(); Compressor compressor = header.getCompressor(); int csize = header.getCSize(); - short subsections = - type.hasFiles() ? header.getSubsections() : 1; CountingInputStream cs = new CountingInputStream(stream, csize); sectionDigest.reset(); DigestInputStream dis = new DigestInputStream(cs, sectionDigest); DataInputStream in = new DataInputStream(dis); for (int subsection = 0; subsection < subsections; subsection++) ! readFile(in, compressor, type, csize); byte[] headerHash = header.getHashNoClone(); checkHashMatch(headerHash, sectionDigest.digest()); ! if (header.getType() != SectionType.SIGNATURE) { calculatedHashes.add(headerHash); } } ! public void readFile(DataInputStream in, ! Compressor compressor, ! SectionType type, ! int csize) throws IOException { switch (compressor) { case NONE: ! if (type == SectionType.MODULE_INFO) { ! moduleInfoBytes = readModuleInfo(in, csize); ! } else if (type == SectionType.SIGNATURE) { ! // Examine the Signature header ! moduleSignatureType = (int)in.readShort(); ! int length = in.readInt(); ! moduleSignatureBytes = readModuleSignature(in, csize - 6); ! if (length != moduleSignatureBytes.length) { ! throw new IOException("Invalid Signature length"); } - } else { - readUncompressedFile(in, type, csize); } ! break; ! case GZIP: ! readGZIPCompressedFile(in, type); ! break; case PACK200_GZIP: ! readClasses( ! new DataInputStream(new CountingInputStream(in, csize))); ! break; default: ! throw new IOException("Unsupported Compressor for files: " + ! compressor); } } ! public void readClasses(DataInputStream in) throws IOException { ! unpack200gzip(in); } private File currentPath = null; ! private OutputStream openOutputStream(SectionType type, ! String path) throws IOException { if (!extract) return new NullOutputStream(); currentPath = null; ! assert type != SectionType.CLASSES; ! if (type == SectionType.RESOURCES) ! return Files.newOutputStream(contentStream(), path); currentPath = computeRealPath(type, path); File parent = currentPath.getParentFile(); if (!parent.exists()) Files.mkdirs(parent, currentPath.getName()); return new BufferedOutputStream(new FileOutputStream(currentPath)); --- 330,540 ---- throws IOException { SectionType type = header.getType(); Compressor compressor = header.getCompressor(); int csize = header.getCSize(); CountingInputStream cs = new CountingInputStream(stream, csize); sectionDigest.reset(); DigestInputStream dis = new DigestInputStream(cs, sectionDigest); DataInputStream in = new DataInputStream(dis); + if (type.hasFiles()) { + short subsections = header.getSubsections(); for (int subsection = 0; subsection < subsections; subsection++) ! readSubSection(in, compressor, type); ! } else if (type == CLASSES) { ! readClassesContent(in, compressor); ! } else { ! readSectionBytes(in, compressor, type); ! } + // ## appropriate exception ?? + if (cs.read() != -1) + throw new IllegalArgumentException("All section content not read"); + byte[] headerHash = header.getHashNoClone(); checkHashMatch(headerHash, sectionDigest.digest()); ! if (header.getType() != SIGNATURE) { calculatedHashes.add(headerHash); } } ! // module-info OR module signature ! public void readSectionBytes(DataInputStream in, Compressor compressor, ! SectionType type) throws IOException { + Decompressor decompressor = Decompressor.newInstance(in, compressor); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + decompressor.extractTo(baos); + + if (type == MODULE_INFO) + moduleInfoBytes = baos.toByteArray(); + else if (type == SIGNATURE) { + SignatureSection sh = SignatureSection.read(new DataInputStream( + new ByteArrayInputStream(baos.toByteArray()))); + moduleSignatureType = sh.getSignatureType(); + moduleSignatureBytes = sh.getSignature(); + } else + throw new IllegalArgumentException( + "Unsupported raw bytes section type: " + type); + } + + public void readClassesContent(DataInputStream in, Compressor compressor) + throws IOException + { + ClassesDecompressor decompressor = + ClassesDecompressor.newInstance(in, compressor, deflate); + decompressor.extractTo(contentStream()); + } + + // subsections/files (resources, libs, cmds, configs) + public void readSubSection(DataInputStream in, Compressor compressor, + SectionType type) + throws IOException + { + assert type == RESOURCES || type == NATIVE_LIBS || + type == NATIVE_CMDS || type == CONFIG; + + SubSectionFileHeader header = SubSectionFileHeader.read(in); + CountingInputStream cs = new CountingInputStream(in, header.getCSize()); + Decompressor decompressor = Decompressor.newInstance(cs, compressor); + String path = header.getPath(); + try (OutputStream sink = openOutputStream(type, path)) { + decompressor.extractTo(sink); + } + + String prefix = type == RESOURCES ? "" : + getSubdirOfSection(type) + File.separator; + contents.add(prefix + path); + // post processing for executable and files outside the module dir + if (extract) + postExtract(type, currentPath); + } + + static class Decompressor { + protected InputStream source; + protected Decompressor() { } + protected Decompressor(InputStream source) { + // no decompression + this.source = source; + } + + void extractTo(OutputStream sink) throws IOException { + copyStream(source, sink); + } + + static Decompressor newInstance(InputStream source, + Compressor compressor) + throws IOException + { switch (compressor) { case NONE: ! return new Decompressor(source); ! case GZIP: ! return new GZIPDecompressor(source); ! default: ! throw new IllegalArgumentException( ! "Unsupported compressor type: " + compressor); ! } ! } ! } ! static class GZIPDecompressor extends Decompressor { ! GZIPDecompressor(InputStream source) throws IOException { ! this.source = new GZIPInputStream(source) { ! public void close() throws IOException {} ! }; } } ! ! static abstract class ClassesDecompressor { ! protected InputStream source; ! ! abstract void extractTo(JarOutputStream sink) throws IOException; ! ! static ClassesDecompressor newInstance(InputStream source, ! Compressor compressor, ! boolean deflate) ! throws IOException ! { ! switch (compressor) { case PACK200_GZIP: ! return new Pack200GZIPDecompressor(source, deflate); default: ! throw new IllegalArgumentException( ! "Unsupported compressor type: " + compressor); } } + } ! static class Pack200GZIPDecompressor extends ClassesDecompressor { ! private Pack200.Unpacker unpacker; ! ! Pack200GZIPDecompressor(InputStream source, boolean deflate) ! throws IOException ! { ! this.source = new GZIPInputStream(source) { ! public void close() throws IOException {} ! }; ! unpacker = Pack200.newUnpacker(); ! if (deflate) { ! Map<String,String> p = unpacker.properties(); ! p.put(Pack200.Unpacker.DEFLATE_HINT, Pack200.Unpacker.TRUE); } + } + void extractTo(JarOutputStream sink) throws IOException { + unpacker.unpack(source, sink); + } + } + + private TrackingJarOutputStream contentStream = null; + + // Used to retrieved archive contents + private static class TrackingJarOutputStream extends JarOutputStream { + private final List<String> contents; + TrackingJarOutputStream(OutputStream out, List<String> contents) + throws IOException + { + super(out); + this.contents = contents; + } + + public void putNextEntry(ZipEntry ze) throws IOException { + super.putNextEntry(ze); + contents.add(ze.getName()); + } + } + + private JarOutputStream contentStream() throws IOException { + if (contentStream != null) + return contentStream; + + OutputStream sink; + if (extract) + sink = new BufferedOutputStream( + new FileOutputStream(computeRealPath("classes"))); + else + sink = new NullOutputStream(); + + return contentStream = new TrackingJarOutputStream(sink, contents); + } + private File currentPath = null; ! private OutputStream openOutputStream(SectionType type, String path) throws IOException { if (!extract) return new NullOutputStream(); + currentPath = null; ! assert type != CLASSES; ! if (type == RESOURCES) ! return Files.newOutputStream(contentStream(), deflate, path); currentPath = computeRealPath(type, path); File parent = currentPath.getParentFile(); if (!parent.exists()) Files.mkdirs(parent, currentPath.getName()); return new BufferedOutputStream(new FileOutputStream(currentPath));
*** 429,504 **** public void write(byte[] b) throws IOException {} @Override public void write(byte[] b, int off, int len) throws IOException {} } ! public void readGZIPCompressedFile(DataInputStream in, ! SectionType type) throws IOException { ! SubSectionFileHeader header = SubSectionFileHeader.read(in); ! int csize = header.getCSize(); ! ! // Splice off the compressed file from input stream ! ByteArrayOutputStream baos = new ByteArrayOutputStream(); ! copyStream(new CountingInputStream(in, csize), baos, csize); ! ! byte[] compressedfile = baos.toByteArray(); ! ByteArrayInputStream bain ! = new ByteArrayInputStream(compressedfile); ! try (GZIPInputStream gin = new GZIPInputStream(bain); ! OutputStream out = openOutputStream(type, header.getPath())) { ! copyStream(gin, out); } - if (extract) - postExtract(type, currentPath); - } - - public void readUncompressedFile(DataInputStream in, - SectionType type, - int csize) - throws IOException - { - assert type != SectionType.MODULE_INFO; - SubSectionFileHeader header = SubSectionFileHeader.read(in); - csize = header.getCSize(); - try (OutputStream out = openOutputStream(type, header.getPath())) { - CountingInputStream cin = new CountingInputStream(in, csize); - byte[] buf = new byte[8192]; - int n; - while ((n = cin.read(buf)) >= 0) - out.write(buf, 0, n); - } - if (extract) { - postExtract(type, currentPath); - } - } - - public byte[] readModuleInfo(DataInputStream in, int csize) - throws IOException - { - CountingInputStream cin = new CountingInputStream(in, csize); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buf = new byte[8192]; - int n; - while ((n = cin.read(buf)) >= 0) - out.write(buf, 0, n); - return out.toByteArray(); - } - - public byte[] readModuleSignature(DataInputStream in, int csize) - throws IOException - { - return readModuleInfo(in, csize); // signature has the same format - } - // Track files installed outside the module library. For later removal. // files are relative to the modules directory. private PrintWriter filesWriter; ! private void trackFiles(SectionType type, File file) throws IOException { if (file == null || file.toPath().startsWith(destination.toPath())) return; --- 547,571 ---- public void write(byte[] b) throws IOException {} @Override public void write(byte[] b, int off, int len) throws IOException {} } ! private static void checkHashMatch(byte[] expected, byte[] computed) throws IOException { ! if (!MessageDigest.isEqual(expected, computed)) ! throw new IOException("Expected hash " ! + hashHexString(expected) ! + " instead of " ! + hashHexString(computed)); } // Track files installed outside the module library. For later removal. // files are relative to the modules directory. private PrintWriter filesWriter; ! private void trackFiles(File file) throws IOException { if (file == null || file.toPath().startsWith(destination.toPath())) return;
*** 541,555 **** return excs; } // Returns the absolute path of the given section type. private File getDirOfSection(SectionType type) { ! if (type == SectionType.NATIVE_LIBS) return natlibs; ! else if (type == SectionType.NATIVE_CMDS) return natcmds; ! else if (type == SectionType.CONFIG) return configs; // resolve sub dir section paths against the modules directory return new File(destination, ModuleFile.getSubdirOfSection(type)); } --- 608,622 ---- return excs; } // Returns the absolute path of the given section type. private File getDirOfSection(SectionType type) { ! if (type == NATIVE_LIBS) return natlibs; ! else if (type == NATIVE_CMDS) return natcmds; ! else if (type == CONFIG) return configs; // resolve sub dir section paths against the modules directory return new File(destination, ModuleFile.getSubdirOfSection(type)); }
*** 576,621 **** } private static void markNativeCodeExecutable(SectionType type, File file) { ! if (type == SectionType.NATIVE_CMDS ! || (type == SectionType.NATIVE_LIBS ! && System.getProperty("os.name").startsWith("Windows"))) ! { file.setExecutable(true); } - } private void postExtract(SectionType type, File path) throws IOException { markNativeCodeExecutable(type, path); ! trackFiles(type, path); } - - private void unpack200gzip(DataInputStream in) throws IOException { - GZIPInputStream gis = new GZIPInputStream(in) { - public void close() throws IOException {} - }; - Pack200.Unpacker unpacker = Pack200.newUnpacker(); - if (deflate) { - Map<String,String> p = unpacker.properties(); - p.put(Pack200.Unpacker.DEFLATE_HINT, Pack200.Unpacker.TRUE); } - unpacker.unpack(gis, contentStream()); - } - } - private static void checkCompressor(SectionType type, Compressor compressor) { ! if ((SectionType.MODULE_INFO == type && ! Compressor.NONE != compressor) ! || (SectionType.CLASSES == type && ! Compressor.PACK200_GZIP != compressor)) throw new IllegalArgumentException(type + " may not use compressor " + compressor); } --- 643,670 ---- } private static void markNativeCodeExecutable(SectionType type, File file) { ! if (type == NATIVE_CMDS || (type == NATIVE_LIBS && ! System.getProperty("os.name").startsWith("Windows"))) file.setExecutable(true); } private void postExtract(SectionType type, File path) throws IOException { markNativeCodeExecutable(type, path); ! trackFiles(path); } } private static void checkCompressor(SectionType type, Compressor compressor) { ! if ((MODULE_INFO == type && Compressor.NONE != compressor) || ! (CLASSES == type && Compressor.PACK200_GZIP != compressor)) throw new IllegalArgumentException(type + " may not use compressor " + compressor); }
*** 627,675 **** + subsections); else if (type.hasFiles() && subsections == 0) throw new IllegalArgumentException(type + " subsection count is 0"); } ! private static void copyStream(InputStream in, DataOutput out) throws IOException { ! ! byte[] buffer = new byte[1024 * 8]; ! for (int b_read = in.read(buffer); ! -1 != b_read; ! b_read = in.read(buffer)) ! out.write(buffer, 0, b_read); } ! private static void copyStream(InputStream in, OutputStream out) throws IOException { ! copyStream(in, (DataOutput) new DataOutputStream(out)); } - private static void copyStream(InputStream in, DataOutput out, - int count) - throws IOException - { - byte[] buffer = new byte[1024 * 8]; - - while(count > 0) { - int b_read = in.read(buffer, 0, Math.min(count, buffer.length)); - if (-1 == b_read) - return; - out.write(buffer, 0, b_read); - count-=b_read; - } - } - - private static void copyStream(InputStream in, OutputStream out, - int count) - throws IOException - { - copyStream(in, (DataOutput) new DataOutputStream(out), count); - } - private static void ensureNonNegativity(long size, String parameter) { if (size < 0) throw new IllegalArgumentException(parameter + "<0: " + size); } --- 676,700 ---- + subsections); else if (type.hasFiles() && subsections == 0) throw new IllegalArgumentException(type + " subsection count is 0"); } ! private static void copyStream(InputStream source, DataOutput sink) throws IOException { ! byte[] buf = new byte[8192]; ! int b_read = 0; ! while((b_read = source.read(buf)) > 0) ! sink.write(buf, 0, b_read); } ! private static void copyStream(InputStream source, OutputStream sink) throws IOException { ! copyStream(source, (DataOutput) new DataOutputStream(sink)); } private static void ensureNonNegativity(long size, String parameter) { if (size < 0) throw new IllegalArgumentException(parameter + "<0: " + size); }
*** 784,794 **** } private static byte[] readHashBytes(DataInputStream in, short hashLength) throws IOException { - final byte[] hash = new byte[hashLength]; in.readFully(hash); return hash; } --- 809,818 ----
*** 798,808 **** } private static byte[] readFileHash(DigestInputStream dis) throws IOException { - DataInputStream in = new DataInputStream(dis); final short hashLength = readHashLength(in); // Turn digest computation off before reading the file hash --- 822,831 ----
*** 1052,1061 **** --- 1075,1130 ---- final String path = in.readUTF(); return new SubSectionFileHeader(csize, path); } } + + public final static class SignatureSection { + private final int signatureType; // One of FileConstants.ModuleFile.HashType + private final int signatureLength; // Length of signature + private final byte[] signature; // Signature bytes + + public int getSignatureType() { + return signatureType; + } + + public int getSignatureLength() { + return signatureLength; + } + + public byte[] getSignature() { + return signature; + } + + public SignatureSection(int signatureType, int signatureLength, + byte[] signature) { + ensureNonNegativity(signatureLength, "signatureLength"); + + this.signatureType = signatureType; + this.signatureLength = signatureLength; + this.signature = signature.clone(); + } + + public void write(DataOutput out) throws IOException { + out.writeShort(signatureType); + out.writeInt(signatureLength); + out.write(signature); + } + + public static SignatureSection read(DataInputStream in) + throws IOException + { + final short signatureType = in.readShort(); + ensureMatch(signatureType, SignatureType.PKCS7.value(), "SignatureType.PKCS7"); + final int signatureLength = in.readInt(); + ensureNonNegativity(signatureLength, "signatureLength"); + final byte[] signature = new byte[signatureLength]; + in.readFully(signature); + return new SignatureSection(signatureType, signatureLength, + signature); + } + } private static void writeHash(DataOutput out, byte[] hash) throws IOException { out.writeShort(hash.length);