• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1996, 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 java.util.zip;
27 
28 import java.io.SequenceInputStream;
29 import java.io.ByteArrayInputStream;
30 import java.io.FilterInputStream;
31 import java.io.InputStream;
32 import java.io.IOException;
33 import java.io.EOFException;
34 
35 /**
36  * This class implements a stream filter for reading compressed data in
37  * the GZIP file format.
38  *
39  * @see         InflaterInputStream
40  * @author      David Connelly
41  *
42  */
43 public
44 class GZIPInputStream extends InflaterInputStream {
45     /**
46      * CRC-32 for uncompressed data.
47      */
48     protected CRC32 crc = new CRC32();
49 
50     /**
51      * Indicates end of input stream.
52      */
53     protected boolean eos;
54 
55     private boolean closed = false;
56 
57     /**
58      * Check to make sure that this stream has not been closed
59      */
ensureOpen()60     private void ensureOpen() throws IOException {
61         if (closed) {
62             throw new IOException("Stream closed");
63         }
64     }
65 
66     /**
67      * Creates a new input stream with the specified buffer size.
68      * @param in the input stream
69      * @param size the input buffer size
70      *
71      * @exception ZipException if a GZIP format error has occurred or the
72      *                         compression method used is unsupported
73      * @exception IOException if an I/O error has occurred
74      * @exception IllegalArgumentException if {@code size <= 0}
75      */
GZIPInputStream(InputStream in, int size)76     public GZIPInputStream(InputStream in, int size) throws IOException {
77         super(in, new Inflater(true), size);
78         // Android-removed: Unconditionally close external inflaters (b/26462400)
79         // usesDefaultInflater = true;
80         // BEGIN Android-changed: Do not rely on finalization to inf.end().
81         // readHeader(in);
82         try {
83             readHeader(in);
84         } catch (Exception e) {
85             inf.end();
86             throw e;
87         }
88         // END Android-changed: Do not rely on finalization to inf.end().
89     }
90 
91     /**
92      * Creates a new input stream with a default buffer size.
93      * @param in the input stream
94      *
95      * @exception ZipException if a GZIP format error has occurred or the
96      *                         compression method used is unsupported
97      * @exception IOException if an I/O error has occurred
98      */
GZIPInputStream(InputStream in)99     public GZIPInputStream(InputStream in) throws IOException {
100         this(in, 512);
101     }
102 
103     /**
104      * Reads uncompressed data into an array of bytes. If <code>len</code> is not
105      * zero, the method will block until some input can be decompressed; otherwise,
106      * no bytes are read and <code>0</code> is returned.
107      * @param buf the buffer into which the data is read
108      * @param off the start offset in the destination array <code>b</code>
109      * @param len the maximum number of bytes read
110      * @return  the actual number of bytes read, or -1 if the end of the
111      *          compressed input stream is reached
112      *
113      * @exception  NullPointerException If <code>buf</code> is <code>null</code>.
114      * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
115      * <code>len</code> is negative, or <code>len</code> is greater than
116      * <code>buf.length - off</code>
117      * @exception ZipException if the compressed input data is corrupt.
118      * @exception IOException if an I/O error has occurred.
119      *
120      */
read(byte[] buf, int off, int len)121     public int read(byte[] buf, int off, int len) throws IOException {
122         ensureOpen();
123         if (eos) {
124             return -1;
125         }
126         int n = super.read(buf, off, len);
127         if (n == -1) {
128             if (readTrailer())
129                 eos = true;
130             else
131                 return this.read(buf, off, len);
132         } else {
133             crc.update(buf, off, n);
134         }
135         return n;
136     }
137 
138     /**
139      * Closes this input stream and releases any system resources associated
140      * with the stream.
141      * @exception IOException if an I/O error has occurred
142      */
close()143     public void close() throws IOException {
144         if (!closed) {
145             super.close();
146             eos = true;
147             closed = true;
148         }
149     }
150 
151     /**
152      * GZIP header magic number.
153      */
154     public final static int GZIP_MAGIC = 0x8b1f;
155 
156     /*
157      * File header flags.
158      */
159     private final static int FTEXT      = 1;    // Extra text
160     private final static int FHCRC      = 2;    // Header CRC
161     private final static int FEXTRA     = 4;    // Extra field
162     private final static int FNAME      = 8;    // File name
163     private final static int FCOMMENT   = 16;   // File comment
164 
165     /*
166      * Reads GZIP member header and returns the total byte number
167      * of this member header.
168      */
readHeader(InputStream this_in)169     private int readHeader(InputStream this_in) throws IOException {
170         CheckedInputStream in = new CheckedInputStream(this_in, crc);
171         crc.reset();
172         // Check header magic
173         if (readUShort(in) != GZIP_MAGIC) {
174             throw new ZipException("Not in GZIP format");
175         }
176         // Check compression method
177         if (readUByte(in) != 8) {
178             throw new ZipException("Unsupported compression method");
179         }
180         // Read flags
181         int flg = readUByte(in);
182         // Skip MTIME, XFL, and OS fields
183         skipBytes(in, 6);
184         int n = 2 + 2 + 6;
185         // Skip optional extra field
186         if ((flg & FEXTRA) == FEXTRA) {
187             int m = readUShort(in);
188             skipBytes(in, m);
189             n += m + 2;
190         }
191         // Skip optional file name
192         if ((flg & FNAME) == FNAME) {
193             do {
194                 n++;
195             } while (readUByte(in) != 0);
196         }
197         // Skip optional file comment
198         if ((flg & FCOMMENT) == FCOMMENT) {
199             do {
200                 n++;
201             } while (readUByte(in) != 0);
202         }
203         // Check optional header CRC
204         if ((flg & FHCRC) == FHCRC) {
205             int v = (int)crc.getValue() & 0xffff;
206             if (readUShort(in) != v) {
207                 throw new ZipException("Corrupt GZIP header");
208             }
209             n += 2;
210         }
211         crc.reset();
212         return n;
213     }
214 
215     /*
216      * Reads GZIP member trailer and returns true if the eos
217      * reached, false if there are more (concatenated gzip
218      * data set)
219      */
readTrailer()220     private boolean readTrailer() throws IOException {
221         InputStream in = this.in;
222         int n = inf.getRemaining();
223         if (n > 0) {
224             in = new SequenceInputStream(
225                         new ByteArrayInputStream(buf, len - n, n),
226                         new FilterInputStream(in) {
227                             public void close() throws IOException {}
228                         });
229         }
230         // Uses left-to-right evaluation order
231         if ((readUInt(in) != crc.getValue()) ||
232             // rfc1952; ISIZE is the input size modulo 2^32
233             (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
234             throw new ZipException("Corrupt GZIP trailer");
235 
236         // If there are more bytes available in "in" or
237         // the leftover in the "inf" is > 26 bytes:
238         // this.trailer(8) + next.header.min(10) + next.trailer(8)
239         // try concatenated case
240         if (this.in.available() > 0 || n > 26) {
241             int m = 8;                  // this.trailer
242             try {
243                 m += readHeader(in);    // next.header
244             } catch (IOException ze) {
245                 return true;  // ignore any malformed, do nothing
246             }
247             inf.reset();
248             if (n > m)
249                 inf.setInput(buf, len - n + m, n - m);
250             return false;
251         }
252         return true;
253     }
254 
255     /*
256      * Reads unsigned integer in Intel byte order.
257      */
readUInt(InputStream in)258     private long readUInt(InputStream in) throws IOException {
259         long s = readUShort(in);
260         return ((long)readUShort(in) << 16) | s;
261     }
262 
263     /*
264      * Reads unsigned short in Intel byte order.
265      */
readUShort(InputStream in)266     private int readUShort(InputStream in) throws IOException {
267         int b = readUByte(in);
268         return (readUByte(in) << 8) | b;
269     }
270 
271     /*
272      * Reads unsigned byte.
273      */
readUByte(InputStream in)274     private int readUByte(InputStream in) throws IOException {
275         int b = in.read();
276         if (b == -1) {
277             throw new EOFException();
278         }
279         if (b < -1 || b > 255) {
280             // Report on this.in, not argument in; see read{Header, Trailer}.
281             throw new IOException(this.in.getClass().getName()
282                 + ".read() returned value out of range -1..255: " + b);
283         }
284         return b;
285     }
286 
287     private byte[] tmpbuf = new byte[128];
288 
289     /*
290      * Skips bytes of input data blocking until all bytes are skipped.
291      * Does not assume that the input stream is capable of seeking.
292      */
skipBytes(InputStream in, int n)293     private void skipBytes(InputStream in, int n) throws IOException {
294         while (n > 0) {
295             int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
296             if (len == -1) {
297                 throw new EOFException();
298             }
299             n -= len;
300         }
301     }
302 }
303