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