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