1 /* 2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.java.util.jar.pack; 27 28 import java.io.BufferedInputStream; 29 import java.io.ByteArrayOutputStream; 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.IOException; 33 import java.io.InputStream; 34 import java.io.OutputStream; 35 import java.time.LocalDateTime; 36 import java.time.ZoneOffset; 37 import java.util.HashSet; 38 import java.util.Set; 39 import java.util.SortedMap; 40 import java.util.jar.JarEntry; 41 import java.util.jar.JarInputStream; 42 import java.util.jar.JarOutputStream; 43 import java.util.jar.Pack200; 44 import java.util.zip.CRC32; 45 import java.util.zip.CheckedOutputStream; 46 import java.util.zip.ZipEntry; 47 48 /* 49 * Implementation of the Pack provider. 50 * </pre></blockquote> 51 * @author John Rose 52 * @author Kumar Srinivasan 53 */ 54 55 56 @SuppressWarnings({"removal"}) 57 public class UnpackerImpl extends TLGlobals implements Pack200.Unpacker { 58 59 public UnpackerImpl() {} 60 61 62 63 /** 64 * Get the set of options for the pack and unpack engines. 65 * @return A sorted association of option key strings to option values. 66 */ 67 public SortedMap<String, String> properties() { 68 return props; 69 } 70 71 // Back-pointer to NativeUnpacker, when active. 72 Object _nunp; 73 74 75 public String toString() { 76 return Utils.getVersionString(); 77 } 78 79 //Driver routines 80 81 // The unpack worker... 82 /** 83 * Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally 84 * the entire buffer must be read, it may be more efficient to read the packed-stream 85 * to a file and pass the File object, in the alternate method described below. 86 * <p> 87 * Closes its input but not its output. (The output can accumulate more elements.) 88 * @param in an InputStream. 89 * @param out a JarOutputStream. 90 * @exception IOException if an error is encountered. 91 */ 92 public synchronized void unpack(InputStream in, JarOutputStream out) throws IOException { 93 if (in == null) { 94 throw new NullPointerException("null input"); 95 } 96 if (out == null) { 97 throw new NullPointerException("null output"); 98 } 99 assert(Utils.currentInstance.get() == null); 100 101 try { 102 Utils.currentInstance.set(this); 103 final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); 104 BufferedInputStream in0 = new BufferedInputStream(in); 105 if (Utils.isJarMagic(Utils.readMagic(in0))) { 106 if (verbose > 0) 107 Utils.log.info("Copying unpacked JAR file..."); 108 Utils.copyJarFile(new JarInputStream(in0), out); 109 } else if (props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) { 110 (new DoUnpack()).run(in0, out); 111 in0.close(); 112 Utils.markJarFile(out); 113 } else { 114 try { 115 (new NativeUnpack(this)).run(in0, out); 116 } catch (UnsatisfiedLinkError | NoClassDefFoundError ex) { 117 // failover to java implementation 118 (new DoUnpack()).run(in0, out); 119 } 120 in0.close(); 121 Utils.markJarFile(out); 122 } 123 } finally { 124 _nunp = null; 125 Utils.currentInstance.set(null); 126 } 127 } 128 129 /** 130 * Takes an input File containing the pack file, and generates a JarOutputStream. 131 * <p> 132 * Does not close its output. (The output can accumulate more elements.) 133 * @param in a File. 134 * @param out a JarOutputStream. 135 * @exception IOException if an error is encountered. 136 */ 137 public synchronized void unpack(File in, JarOutputStream out) throws IOException { 138 if (in == null) { 139 throw new NullPointerException("null input"); 140 } 141 if (out == null) { 142 throw new NullPointerException("null output"); 143 } 144 // Use the stream-based implementation. 145 // %%% Reconsider if native unpacker learns to memory-map the file. 146 try (FileInputStream instr = new FileInputStream(in)) { 147 unpack(instr, out); 148 } 149 if (props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) { 150 in.delete(); 151 } 152 } 153 154 private class DoUnpack { 155 final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); 156 157 { 158 props.setInteger(Pack200.Unpacker.PROGRESS, 0); 159 } 160 161 // Here's where the bits are read from disk: 162 final Package pkg = new Package(); 163 164 final boolean keepModtime 165 = Pack200.Packer.KEEP.equals( 166 props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP)); 167 final boolean keepDeflateHint 168 = Pack200.Packer.KEEP.equals( 169 props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP)); 170 final int modtime; 171 final boolean deflateHint; 172 { 173 if (!keepModtime) { 174 modtime = props.getTime(Utils.UNPACK_MODIFICATION_TIME); 175 } else { 176 modtime = pkg.default_modtime; 177 } 178 179 deflateHint = (keepDeflateHint) ? false : 180 props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT); 181 } 182 183 // Checksum apparatus. 184 final CRC32 crc = new CRC32(); 185 final ByteArrayOutputStream bufOut = new ByteArrayOutputStream(); 186 final OutputStream crcOut = new CheckedOutputStream(bufOut, crc); 187 188 public void run(BufferedInputStream in, JarOutputStream out) throws IOException { 189 if (verbose > 0) { 190 props.list(System.out); 191 } 192 for (int seg = 1; ; seg++) { 193 unpackSegment(in, out); 194 195 // Try to get another segment. 196 if (!Utils.isPackMagic(Utils.readMagic(in))) break; 197 if (verbose > 0) 198 Utils.log.info("Finished segment #"+seg); 199 } 200 } 201 202 private void unpackSegment(InputStream in, JarOutputStream out) throws IOException { 203 props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0"); 204 // Process the output directory or jar output. 205 new PackageReader(pkg, in).read(); 206 207 if (props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug"); 208 if (props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile"); 209 props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50"); 210 pkg.ensureAllClassFiles(); 211 // Now write out the files. 212 Set<Package.Class> classesToWrite = new HashSet<>(pkg.getClasses()); 213 for (Package.File file : pkg.getFiles()) { 214 String name = file.nameString; 215 JarEntry je = new JarEntry(Utils.getJarEntryName(name)); 216 boolean deflate; 217 218 deflate = (keepDeflateHint) 219 ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) || 220 ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0)) 221 : deflateHint; 222 223 boolean needCRC = !deflate; // STORE mode requires CRC 224 225 if (needCRC) crc.reset(); 226 bufOut.reset(); 227 if (file.isClassStub()) { 228 Package.Class cls = file.getStubClass(); 229 assert(cls != null); 230 new ClassWriter(cls, needCRC ? crcOut : bufOut).write(); 231 classesToWrite.remove(cls); // for an error check 232 } else { 233 // collect data & maybe CRC 234 file.writeTo(needCRC ? crcOut : bufOut); 235 } 236 je.setMethod(deflate ? JarEntry.DEFLATED : JarEntry.STORED); 237 if (needCRC) { 238 if (verbose > 0) 239 Utils.log.info("stored size="+bufOut.size()+" and crc="+crc.getValue()); 240 241 je.setMethod(JarEntry.STORED); 242 je.setSize(bufOut.size()); 243 je.setCrc(crc.getValue()); 244 } 245 if (keepModtime) { 246 LocalDateTime ldt = LocalDateTime 247 .ofEpochSecond(file.modtime, 0, ZoneOffset.UTC); 248 je.setTimeLocal(ldt); 249 } else { 250 je.setTime((long)modtime * 1000); 251 } 252 out.putNextEntry(je); 253 bufOut.writeTo(out); 254 out.closeEntry(); 255 if (verbose > 0) 256 Utils.log.info("Writing "+Utils.zeString((ZipEntry)je)); 257 } 258 assert(classesToWrite.isEmpty()); 259 props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100"); 260 pkg.reset(); // reset for the next segment, if any 261 } 262 } 263 }