1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.util.zip; 28 29 import java.io.FilterInputStream; 30 import java.io.InputStream; 31 import java.io.IOException; 32 import java.io.EOFException; 33 34 /** 35 * This class implements a stream filter for uncompressing data in the 36 * "deflate" compression format. It is also used as the basis for other 37 * decompression filters, such as GZIPInputStream. 38 * 39 * @see Inflater 40 * @author David Connelly 41 * @since 1.1 42 */ 43 public class InflaterInputStream extends FilterInputStream { 44 /** 45 * Decompressor for this stream. 46 */ 47 protected Inflater inf; 48 49 /** 50 * Input buffer for decompression. 51 */ 52 protected byte[] buf; 53 54 /** 55 * Length of input buffer. 56 */ 57 protected int len; 58 59 // Android-changed: Make closed accessible to subclasses. 60 // This was made protected because it needed to be accessed by 61 // StrictJarFile.ZipInflaterInputStream. Unfortunately, it was not marked as @hide and so it 62 // inadvertently became part of the public API. It will be marked as @removed to remove it from 63 // the public API in a future release of Android. See http://b/111592689 for more information. 64 // private boolean closed = false; 65 /** 66 * Indicates whether the {@link #close()} method has been called, internal use only. 67 * 68 * @deprecated This field will be removed from a future version of Android and should not be 69 * used. Subclasses that access this field need to be modified to keep track of their own 70 * closed state by overriding close(). 71 */ 72 @Deprecated 73 protected boolean closed = false; 74 75 // this flag is set to true after EOF has reached 76 private boolean reachEOF = false; 77 78 /** 79 * Check to make sure that this stream has not been closed 80 */ ensureOpen()81 private void ensureOpen() throws IOException { 82 if (closed) { 83 throw new IOException("Stream closed"); 84 } 85 } 86 87 // Android-added: constructor which explicitly sets whether Inflater is owned by this stream. 88 /** 89 * Creates a new input stream with the specified decompressor and 90 * buffer size. 91 * @param in the input stream 92 * @param inf the decompressor ("inflater") 93 * @param size the input buffer size 94 * @param ownsInflater whether this {@code InflaterInputStream} controls its inflater 95 * lifetime and should call {@link Inflater#end} when it is closed. 96 * @throws IllegalArgumentException if {@code size <= 0} 97 * @hide 98 */ InflaterInputStream(InputStream in, Inflater inf, int size, boolean ownsInflater)99 InflaterInputStream(InputStream in, Inflater inf, int size, boolean ownsInflater) { 100 super(in); 101 if (in == null || inf == null) { 102 throw new NullPointerException(); 103 } else if (size <= 0) { 104 throw new IllegalArgumentException("buffer size <= 0"); 105 } 106 this.inf = inf; 107 buf = new byte[size]; 108 this.ownsInflater = ownsInflater; 109 } 110 111 /** 112 * Creates a new input stream with the specified decompressor and 113 * buffer size. 114 * @param in the input stream 115 * @param inf the decompressor ("inflater") 116 * @param size the input buffer size 117 * @throws IllegalArgumentException if {@code size <= 0} 118 */ InflaterInputStream(InputStream in, Inflater inf, int size)119 public InflaterInputStream(InputStream in, Inflater inf, int size) { 120 // Android-changed: refer initialization to constructor which specifies ownInflater. 121 /* 122 super(in); 123 if (in == null || inf == null) { 124 throw new NullPointerException(); 125 } else if (size <= 0) { 126 throw new IllegalArgumentException("buffer size <= 0"); 127 } 128 this.inf = inf; 129 buf = new byte[size]; 130 */ 131 this(in, inf, size, /* ownsInflater= */ true); 132 } 133 134 /** 135 * Creates a new input stream with the specified decompressor and a 136 * default buffer size. 137 * @param in the input stream 138 * @param inf the decompressor ("inflater") 139 */ InflaterInputStream(InputStream in, Inflater inf)140 public InflaterInputStream(InputStream in, Inflater inf) { 141 this(in, inf, 512); 142 } 143 144 // Android-changed: Unconditionally close external inflaters (b/26462400) 145 // See http://b/111630946 for more details. 146 // boolean usesDefaultInflater = false; 147 148 // Android-added: functionally this is identical to usesDefaultInflater, but re-using 149 // it will be confusing. Setting it to true keeps old Android behaviour. 150 // This is added just for ZipFileInflaterInputStream - moving to usesDefaultInflater 151 // is trickier. 152 private final boolean ownsInflater; 153 154 /** 155 * Creates a new input stream with a default decompressor and buffer size. 156 * @param in the input stream 157 */ InflaterInputStream(InputStream in)158 public InflaterInputStream(InputStream in) { 159 this(in, in != null ? new Inflater() : null); 160 // Android-changed: Unconditionally close external inflaters (b/26462400) 161 // usesDefaultInflater = true; 162 } 163 164 private byte[] singleByteBuf = new byte[1]; 165 166 /** 167 * Reads a byte of uncompressed data. This method will block until 168 * enough input is available for decompression. 169 * @return the byte read, or -1 if end of compressed input is reached 170 * @throws IOException if an I/O error has occurred 171 */ read()172 public int read() throws IOException { 173 ensureOpen(); 174 return read(singleByteBuf, 0, 1) == -1 ? -1 : Byte.toUnsignedInt(singleByteBuf[0]); 175 } 176 177 /** 178 * Reads uncompressed data into an array of bytes. If {@code len} is not 179 * zero, the method will block until some input can be decompressed; otherwise, 180 * no bytes are read and {@code 0} is returned. 181 * @param b the buffer into which the data is read 182 * @param off the start offset in the destination array {@code b} 183 * @param len the maximum number of bytes read 184 * @return the actual number of bytes read, or -1 if the end of the 185 * compressed input is reached or a preset dictionary is needed 186 * @throws NullPointerException If {@code b} is {@code null}. 187 * @throws IndexOutOfBoundsException If {@code off} is negative, 188 * {@code len} is negative, or {@code len} is greater than 189 * {@code b.length - off} 190 * @throws ZipException if a ZIP format error has occurred 191 * @throws IOException if an I/O error has occurred 192 */ read(byte[] b, int off, int len)193 public int read(byte[] b, int off, int len) throws IOException { 194 ensureOpen(); 195 if (b == null) { 196 throw new NullPointerException(); 197 } else if (off < 0 || len < 0 || len > b.length - off) { 198 throw new IndexOutOfBoundsException(); 199 } else if (len == 0) { 200 return 0; 201 } 202 try { 203 int n; 204 while ((n = inf.inflate(b, off, len)) == 0) { 205 if (inf.finished() || inf.needsDictionary()) { 206 reachEOF = true; 207 return -1; 208 } 209 if (inf.needsInput()) { 210 fill(); 211 } 212 } 213 return n; 214 } catch (DataFormatException e) { 215 String s = e.getMessage(); 216 throw new ZipException(s != null ? s : "Invalid ZLIB data format"); 217 } 218 } 219 220 /** 221 * Returns 0 after EOF has been reached, otherwise always return 1. 222 * <p> 223 * Programs should not count on this method to return the actual number 224 * of bytes that could be read without blocking. 225 * 226 * @return 1 before EOF and 0 after EOF. 227 * @throws IOException if an I/O error occurs. 228 * 229 */ available()230 public int available() throws IOException { 231 ensureOpen(); 232 if (reachEOF) { 233 return 0; 234 } else if (inf.finished()) { 235 // the end of the compressed data stream has been reached 236 reachEOF = true; 237 return 0; 238 } else { 239 return 1; 240 } 241 } 242 243 private byte[] b = new byte[512]; 244 245 /** 246 * Skips specified number of bytes of uncompressed data. 247 * @param n the number of bytes to skip 248 * @return the actual number of bytes skipped. 249 * @throws IOException if an I/O error has occurred 250 * @throws IllegalArgumentException if {@code n < 0} 251 */ skip(long n)252 public long skip(long n) throws IOException { 253 if (n < 0) { 254 throw new IllegalArgumentException("negative skip length"); 255 } 256 ensureOpen(); 257 int max = (int)Math.min(n, Integer.MAX_VALUE); 258 int total = 0; 259 while (total < max) { 260 int len = max - total; 261 if (len > b.length) { 262 len = b.length; 263 } 264 len = read(b, 0, len); 265 if (len == -1) { 266 reachEOF = true; 267 break; 268 } 269 total += len; 270 } 271 return total; 272 } 273 274 /** 275 * Closes this input stream and releases any system resources associated 276 * with the stream. 277 * @throws IOException if an I/O error has occurred 278 */ close()279 public void close() throws IOException { 280 if (!closed) { 281 // BEGIN Android-changed: close inflater only if it is owned by this 282 // InflaterInputStream. (b/26462400) 283 /* 284 if (usesDefaultInflater) 285 inf.end(); 286 */ 287 if (ownsInflater) { 288 inf.end(); 289 } 290 // END Android-changed: close inflater only if it is owned by this 291 // InflaterInputStream. (b/26462400) 292 in.close(); 293 closed = true; 294 } 295 } 296 297 /** 298 * Fills input buffer with more data to decompress. 299 * @throws IOException if an I/O error has occurred 300 */ fill()301 protected void fill() throws IOException { 302 ensureOpen(); 303 len = in.read(buf, 0, buf.length); 304 if (len == -1) { 305 throw new EOFException("Unexpected end of ZLIB input stream"); 306 } 307 inf.setInput(buf, 0, len); 308 } 309 310 /** 311 * Tests if this input stream supports the {@code mark} and 312 * {@code reset} methods. The {@code markSupported} 313 * method of {@code InflaterInputStream} returns 314 * {@code false}. 315 * 316 * @return a {@code boolean} indicating if this stream type supports 317 * the {@code mark} and {@code reset} methods. 318 * @see java.io.InputStream#mark(int) 319 * @see java.io.InputStream#reset() 320 */ markSupported()321 public boolean markSupported() { 322 return false; 323 } 324 325 /** 326 * Marks the current position in this input stream. 327 * 328 * <p> The {@code mark} method of {@code InflaterInputStream} 329 * does nothing. 330 * 331 * @param readlimit the maximum limit of bytes that can be read before 332 * the mark position becomes invalid. 333 * @see java.io.InputStream#reset() 334 */ mark(int readlimit)335 public synchronized void mark(int readlimit) { 336 } 337 338 /** 339 * Repositions this stream to the position at the time the 340 * {@code mark} method was last called on this input stream. 341 * 342 * <p> The method {@code reset} for class 343 * {@code InflaterInputStream} does nothing except throw an 344 * {@code IOException}. 345 * 346 * @throws IOException if this method is invoked. 347 * @see java.io.InputStream#mark(int) 348 * @see java.io.IOException 349 */ reset()350 public synchronized void reset() throws IOException { 351 throw new IOException("mark/reset not supported"); 352 } 353 } 354