1 /*
   2  * Copyright (c) 2003, 2011, 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 com.sun.java.util.jar.pack.ConstantPool.ClassEntry;
  29 import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry;
  30 import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry;
  31 import com.sun.java.util.jar.pack.ConstantPool.MemberEntry;
  32 import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry;
  33 import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry;
  34 import java.io.BufferedInputStream;
  35 import java.io.BufferedOutputStream;
  36 import java.io.File;
  37 import java.io.FilterOutputStream;
  38 import java.io.IOException;
  39 import java.io.InputStream;
  40 import java.io.OutputStream;
  41 import java.util.Date;
  42 import java.util.Enumeration;
  43 import java.util.Map;
  44 import java.util.jar.JarEntry;
  45 import java.util.jar.JarFile;
  46 import java.util.jar.JarInputStream;
  47 import java.util.jar.JarOutputStream;
  48 import java.util.zip.ZipEntry;
  49 import sun.util.logging.PlatformLogger;
  50 
  51 class Utils {
  52     static final String COM_PREFIX = "com.sun.java.util.jar.pack.";
  53     static final String METAINF    = "META-INF";
  54 
  55     /*
  56      * Outputs various diagnostic support information.
  57      * If >0, print summary comments (e.g., constant pool info).
  58      * If >1, print unit comments (e.g., processing of classes).
  59      * If >2, print many comments (e.g., processing of members).
  60      * If >3, print tons of comments (e.g., processing of references).
  61      * (installer only)
  62      */
  63     static final String DEBUG_VERBOSE = Utils.COM_PREFIX+"verbose";
  64 
  65     /*
  66      * Disables use of native code, prefers the Java-coded implementation.
  67      * (installer only)
  68      */
  69     static final String DEBUG_DISABLE_NATIVE = COM_PREFIX+"disable.native";
  70 
  71     /*
  72      * Use the default working TimeZone instead of UTC.
  73      * Note: This has installer unpacker implications.
  74      * see: zip.cpp which uses gmtime vs. localtime.
  75      */
  76     static final String PACK_DEFAULT_TIMEZONE = COM_PREFIX+"default.timezone";
  77 
  78     /*
  79      * Property indicating that the unpacker should
  80      * ignore the transmitted PACK_MODIFICATION_TIME,
  81      * replacing it by the given value. The value can
  82      * be a numeric string, representing the number of
  83      * mSecs since the epoch (UTC), or the special string
  84      * {@link #NOW}, meaning the current time (UTC).
  85      * The default value is the special string {@link #KEEP},
  86      * which asks the unpacker to preserve all transmitted
  87      * modification time information.
  88      * (installer only)
  89      */
  90     static final String UNPACK_MODIFICATION_TIME = COM_PREFIX+"unpack.modification.time";
  91 
  92     /*
  93      * Property indicating that the unpacker strip the
  94      * Debug Attributes, if they are present, in the pack stream.
  95      * The default value is false.
  96      * (installer only)
  97      */
  98     static final String UNPACK_STRIP_DEBUG = COM_PREFIX+"unpack.strip.debug";
  99 
 100     /*
 101      * Remove the input file after unpacking.
 102      * (installer only)
 103      */
 104     static final String UNPACK_REMOVE_PACKFILE = COM_PREFIX+"unpack.remove.packfile";
 105 
 106     /*
 107      * A possible value for MODIFICATION_TIME
 108      */
 109     static final String NOW                             = "now";
 110     // Other debug options:
 111     //   com...debug.bands=false      add band IDs to pack file, to verify sync
 112     //   com...dump.bands=false       dump band contents to local disk
 113     //   com...no.vary.codings=false  turn off coding variation heuristics
 114     //   com...no.big.strings=false   turn off "big string" feature
 115 
 116     /*
 117      * If this property is set to {@link #TRUE}, the packer will preserve
 118      * the ordering of class files of the original jar in the output archive.
 119      * The ordering is preserved only for class-files; resource files
 120      * may be reordered.
 121      * <p>
 122      * If the packer is allowed to reorder class files, it can marginally
 123      * decrease the transmitted size of the archive.
 124      */
 125     static final String PACK_KEEP_CLASS_ORDER = COM_PREFIX+"keep.class.order";
 126     /*
 127      * This string PACK200 is given as a zip comment on all JAR files
 128      * produced by this utility.
 129      */
 130     static final String PACK_ZIP_ARCHIVE_MARKER_COMMENT = "PACK200";
 131 
 132     // Keep a TLS point to the global data and environment.
 133     // This makes it simpler to supply environmental options
 134     // to the engine code, especially the native code.
 135     static final ThreadLocal<TLGlobals> currentInstance = new ThreadLocal<>();
 136 
 137     // convenience methods to access the TL globals
 138     static TLGlobals getTLGlobals() {
 139         return currentInstance.get();
 140     }
 141 
 142     static Map<String, Utf8Entry> getUtf8Entries() {
 143         return getTLGlobals().getUtf8Entries();
 144     }
 145 
 146     static Map<String, ClassEntry> getClassEntries() {
 147         return getTLGlobals().getClassEntries();
 148     }
 149 
 150     static Map<Object, LiteralEntry> getLiteralEntries() {
 151         return getTLGlobals().getLiteralEntries();
 152     }
 153 
 154     static Map<String, DescriptorEntry> getDescriptorEntries() {
 155          return getTLGlobals().getDescriptorEntries();
 156     }
 157 
 158     static Map<String, SignatureEntry> getSignatureEntries() {
 159         return getTLGlobals().getSignatureEntries();
 160     }
 161 
 162     static Map<String, MemberEntry> getMemberEntries() {
 163         return getTLGlobals().getMemberEntries();
 164     }
 165 
 166     static PropMap currentPropMap() {
 167         Object obj = currentInstance.get();
 168         if (obj instanceof PackerImpl)
 169             return ((PackerImpl)obj).props;
 170         if (obj instanceof UnpackerImpl)
 171             return ((UnpackerImpl)obj).props;
 172         return null;
 173     }
 174 
 175     static final boolean nolog
 176         = Boolean.getBoolean(Utils.COM_PREFIX+"nolog");
 177 
 178 
 179     static class Pack200Logger {
 180         private final String name;
 181         private PlatformLogger log;
 182         Pack200Logger(String name) {
 183             this.name = name;
 184         }
 185 
 186         private synchronized PlatformLogger getLogger() {
 187             if (log == null) {
 188                 log = PlatformLogger.getLogger(name);
 189             }
 190             return log;
 191         }
 192 
 193         public void warning(String msg, Object param) {
 194                 getLogger().warning(msg, param);
 195             }
 196 
 197         public void warning(String msg) {
 198             warning(msg, null);
 199         }
 200 
 201         public void info(String msg) {
 202             int verbose = currentPropMap().getInteger(DEBUG_VERBOSE);
 203             if (verbose > 0) {
 204                 if (nolog) {
 205                     System.out.println(msg);
 206                 } else {
 207                     getLogger().info(msg);
 208                 }
 209             }
 210         }
 211 
 212         public void fine(String msg) {
 213             int verbose = currentPropMap().getInteger(DEBUG_VERBOSE);
 214             if (verbose > 0) {
 215                     System.out.println(msg);
 216             }
 217         }
 218     }
 219 
 220     static final Pack200Logger log
 221         = new Pack200Logger("java.util.jar.Pack200");
 222 
 223     // Returns the Max Version String of this implementation
 224     static String getVersionString() {
 225         return "Pack200, Vendor: " +
 226             System.getProperty("java.vendor") +
 227             ", Version: " +
 228             Constants.JAVA6_PACKAGE_MAJOR_VERSION + "." +
 229             Constants.JAVA6_PACKAGE_MINOR_VERSION;
 230     }
 231 
 232     static void markJarFile(JarOutputStream out) throws IOException {
 233         out.setComment(PACK_ZIP_ARCHIVE_MARKER_COMMENT);
 234     }
 235 
 236     // -0 mode helper
 237     static void copyJarFile(JarInputStream in, JarOutputStream out) throws IOException {
 238         if (in.getManifest() != null) {
 239             ZipEntry me = new ZipEntry(JarFile.MANIFEST_NAME);
 240             out.putNextEntry(me);
 241             in.getManifest().write(out);
 242             out.closeEntry();
 243         }
 244         byte[] buffer = new byte[1 << 14];
 245         for (JarEntry je; (je = in.getNextJarEntry()) != null; ) {
 246             out.putNextEntry(je);
 247             for (int nr; 0 < (nr = in.read(buffer)); ) {
 248                 out.write(buffer, 0, nr);
 249             }
 250         }
 251         in.close();
 252         markJarFile(out);  // add PACK200 comment
 253     }
 254     static void copyJarFile(JarFile in, JarOutputStream out) throws IOException {
 255         byte[] buffer = new byte[1 << 14];
 256         for (Enumeration e = in.entries(); e.hasMoreElements(); ) {
 257             JarEntry je = (JarEntry) e.nextElement();
 258             out.putNextEntry(je);
 259             InputStream ein = in.getInputStream(je);
 260             for (int nr; 0 < (nr = ein.read(buffer)); ) {
 261                 out.write(buffer, 0, nr);
 262             }
 263         }
 264         in.close();
 265         markJarFile(out);  // add PACK200 comment
 266     }
 267     static void copyJarFile(JarInputStream in, OutputStream out) throws IOException {
 268         // 4947205 : Peformance is slow when using pack-effort=0
 269         out = new BufferedOutputStream(out);
 270         out = new NonCloser(out); // protect from JarOutputStream.close()
 271         try (JarOutputStream jout = new JarOutputStream(out)) {
 272             copyJarFile(in, jout);
 273         }
 274     }
 275     static void copyJarFile(JarFile in, OutputStream out) throws IOException {
 276 
 277         // 4947205 : Peformance is slow when using pack-effort=0
 278         out = new BufferedOutputStream(out);
 279         out = new NonCloser(out); // protect from JarOutputStream.close()
 280         try (JarOutputStream jout = new JarOutputStream(out)) {
 281             copyJarFile(in, jout);
 282         }
 283     }
 284         // Wrapper to prevent closing of client-supplied stream.
 285     static private
 286     class NonCloser extends FilterOutputStream {
 287         NonCloser(OutputStream out) { super(out); }
 288         public void close() throws IOException { flush(); }
 289     }
 290    static String getJarEntryName(String name) {
 291         if (name == null)  return null;
 292         return name.replace(File.separatorChar, '/');
 293     }
 294 
 295     static String zeString(ZipEntry ze) {
 296         int store = (ze.getCompressedSize() > 0) ?
 297             (int)( (1.0 - ((double)ze.getCompressedSize()/(double)ze.getSize()))*100 )
 298             : 0 ;
 299         // Follow unzip -lv output
 300         return ze.getSize() + "\t" + ze.getMethod()
 301             + "\t" + ze.getCompressedSize() + "\t"
 302             + store + "%\t"
 303             + new Date(ze.getTime()) + "\t"
 304             + Long.toHexString(ze.getCrc()) + "\t"
 305             + ze.getName() ;
 306     }
 307 
 308 
 309 
 310     static byte[] readMagic(BufferedInputStream in) throws IOException {
 311         in.mark(4);
 312         byte[] magic = new byte[4];
 313         for (int i = 0; i < magic.length; i++) {
 314             // read 1 byte at a time, so we always get 4
 315             if (1 != in.read(magic, i, 1))
 316                 break;
 317         }
 318         in.reset();
 319         return magic;
 320     }
 321 
 322     // magic number recognizers
 323     static boolean isJarMagic(byte[] magic) {
 324         return (magic[0] == (byte)'P' &&
 325                 magic[1] == (byte)'K' &&
 326                 magic[2] >= 1 &&
 327                 magic[2] <  8 &&
 328                 magic[3] == magic[2] + 1);
 329     }
 330     static boolean isPackMagic(byte[] magic) {
 331         return (magic[0] == (byte)0xCA &&
 332                 magic[1] == (byte)0xFE &&
 333                 magic[2] == (byte)0xD0 &&
 334                 magic[3] == (byte)0x0D);
 335     }
 336     static boolean isGZIPMagic(byte[] magic) {
 337         return (magic[0] == (byte)0x1F &&
 338                 magic[1] == (byte)0x8B &&
 339                 magic[2] == (byte)0x08);
 340         // fourth byte is variable "flg" field
 341     }
 342 
 343     private Utils() { } // do not instantiate
 344 }