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