1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* 2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Licensed to the Apache Software Foundation (ASF) under one or more 3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * contributor license agreements. See the NOTICE file distributed with 4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * this work for additional information regarding copyright ownership. 5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The ASF licenses this file to You under the Apache License, Version 2.0 6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (the "License"); you may not use this file except in compliance with 7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the License. You may obtain a copy of the License at 8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 9f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and 15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License. 16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage java.util.zip; 19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 20f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstromimport dalvik.system.CloseGuard; 21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.BufferedInputStream; 229902f3494c6d983879d8b9cfe6b1f771cfefe703Elliott Hughesimport java.io.Closeable; 23739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughesimport java.io.DataInputStream; 24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.File; 25adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.IOException; 26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.InputStream; 27adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.io.RandomAccessFile; 2843a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughesimport java.nio.ByteOrder; 29820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughesimport java.nio.charset.StandardCharsets; 30adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Enumeration; 31f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilsonimport java.util.Iterator; 327365de1056414750d0a7d1fdd26025fd247f0d04Jesse Wilsonimport java.util.LinkedHashMap; 3343a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughesimport libcore.io.BufferIterator; 3443a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughesimport libcore.io.HeapBufferIterator; 35a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffinimport libcore.io.IoUtils; 36ff8234c90ecab9f1db368924bf92a5b16460f9b5Elliott Hughesimport libcore.io.Streams; 37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/** 39f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * This class provides random read access to a zip file. You pay more to read 4013f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * the zip file's central directory up front (from the constructor), but if you're using 4113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * {@link #getEntry} to look up multiple files by name, you get the benefit of this index. 4257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 433d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root * <p>If you only want to iterate through all the files (using {@link #entries()}, you should 4413f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * consider {@link ZipInputStream}, which provides stream-like read access to a zip file and 45f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * has a lower up-front cost because you don't pay to build an in-memory index. 4613f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * 4713f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * <p>If you want to create a zip file, use {@link ZipOutputStream}. There is no API for updating 4813f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * an existing zip file. 49adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 509902f3494c6d983879d8b9cfe6b1f771cfefe703Elliott Hughespublic class ZipFile implements Closeable, ZipConstants { 511d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes /** 527a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * General Purpose Bit Flags, Bit 0. 537a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * If set, indicates that the file is encrypted. 547a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh */ 557a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh static final int GPBF_ENCRYPTED_FLAG = 1 << 0; 567a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh 577a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh /** 581d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * General Purpose Bit Flags, Bit 3. 591d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * If this bit is set, the fields crc-32, compressed 601d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * size and uncompressed size are set to zero in the 611d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * local header. The correct values are put in the 621d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * data descriptor immediately following the compressed 631d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * data. (Note: PKZIP version 2.04g for DOS only 641d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * recognizes this bit for method 8 compression, newer 651d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * versions of PKZIP recognize this bit for any 661d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * compression method.) 671d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes */ 68fb0ec0e650bf8be35acb0d47da0311a7c446aa33Elliott Hughes static final int GPBF_DATA_DESCRIPTOR_FLAG = 1 << 3; 691d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes 701d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes /** 711d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * General Purpose Bit Flags, Bit 11. 721d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * Language encoding flag (EFS). If this bit is set, 731d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * the filename and comment fields for this file 741d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes * must be encoded using UTF-8. 751d8bb71cc4f69089311416a8402f492b7af4784dElliott Hughes */ 76fb0ec0e650bf8be35acb0d47da0311a7c446aa33Elliott Hughes static final int GPBF_UTF8_FLAG = 1 << 11; 77adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 78adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 796aed748da1deaa820d574446037401893d73be9bWilliam Luh * Supported General Purpose Bit Flags Mask. 807a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * Bit mask of bits not supported. 817a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * Note: The only bit that we will enforce at this time 827a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * is the encrypted bit. Although other bits are not supported, 837a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * we must not enforce them as this could break some legitimate 847a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh * use cases (See http://b/8617715). 856aed748da1deaa820d574446037401893d73be9bWilliam Luh */ 867a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh static final int GPBF_UNSUPPORTED_MASK = GPBF_ENCRYPTED_FLAG; 876aed748da1deaa820d574446037401893d73be9bWilliam Luh 886aed748da1deaa820d574446037401893d73be9bWilliam Luh /** 89f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * Open zip file for reading. 90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public static final int OPEN_READ = 1; 92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 94f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * Delete zip file when closed. 95adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public static final int OPEN_DELETE = 4; 97adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 98f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes private final String filename; 99f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 100f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes private File fileToDeleteOnClose; 101f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 102f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes private RandomAccessFile raf; 103f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 104f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes private final LinkedHashMap<String, ZipEntry> entries = new LinkedHashMap<String, ZipEntry>(); 105f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 106820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes private String comment; 107820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes 10812f2d8e2760b78c673b7a187b9062b3938a03147Brian Carlstrom private final CloseGuard guard = CloseGuard.get(); 109f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom 110adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 11113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * Constructs a new {@code ZipFile} allowing read access to the contents of the given file. 112e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * 113e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * <p>UTF-8 is used to decode all comments and entry names in the file. 114e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * 115f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * @throws ZipException if a zip error occurs. 11613f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * @throws IOException if an {@code IOException} occurs. 117adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public ZipFile(File file) throws ZipException, IOException { 11963744c884dd4b4f4307f2b021fb894af164972afElliott Hughes this(file, OPEN_READ); 120e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller } 121e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller 122e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller /** 123e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * Constructs a new {@code ZipFile} allowing read access to the contents of the given file. 124e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * 125e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * <p>UTF-8 is used to decode all comments and entry names in the file. 126e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * 12713f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * @throws IOException if an IOException occurs. 12813f30b9167639ad63da1707102db6320e8f76474Elliott Hughes */ 12913f30b9167639ad63da1707102db6320e8f76474Elliott Hughes public ZipFile(String name) throws IOException { 13063744c884dd4b4f4307f2b021fb894af164972afElliott Hughes this(new File(name), OPEN_READ); 13113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes } 13213f30b9167639ad63da1707102db6320e8f76474Elliott Hughes 13313f30b9167639ad63da1707102db6320e8f76474Elliott Hughes /** 13413f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * Constructs a new {@code ZipFile} allowing access to the given file. 13557995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 136e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * <p>UTF-8 is used to decode all comments and entry names in the file. 137e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * 138e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * <p>The {@code mode} must be either {@code OPEN_READ} or {@code OPEN_READ|OPEN_DELETE}. 139e3d756c5dae1af2aa5f0ad8bc7f133df3e7401ebNeil Fuller * If the {@code OPEN_DELETE} flag is supplied, the file will be deleted at or before the 14013f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * time that the {@code ZipFile} is closed (the contents will remain accessible until 14113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * this {@code ZipFile} is closed); it also calls {@code File.deleteOnExit}. 14213f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * 14313f30b9167639ad63da1707102db6320e8f76474Elliott Hughes * @throws IOException if an {@code IOException} occurs. 144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public ZipFile(File file, int mode) throws IOException { 146f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes filename = file.getPath(); 147f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) { 148cff1616012dc0d56c2da9af2b9b1183e76c7e044Elliott Hughes throw new IllegalArgumentException("Bad mode: " + mode); 149f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 150f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 151f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if ((mode & OPEN_DELETE) != 0) { 152f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes fileToDeleteOnClose = file; 153f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes fileToDeleteOnClose.deleteOnExit(); 15457995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson } else { 155f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes fileToDeleteOnClose = null; 15657995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson } 15757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson 158f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes raf = new RandomAccessFile(filename, "r"); 159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 160a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin // Make sure to close the RandomAccessFile if reading the central directory fails. 161a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin boolean mustCloseFile = true; 162a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin try { 163a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin readCentralDir(); 164a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin 165a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin // Read succeeded so do not close the underlying RandomAccessFile. 166a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin mustCloseFile = false; 167a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin } finally { 168a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin if (mustCloseFile) { 169a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin IoUtils.closeQuietly(raf); 170a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin } 171a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin } 172a6f350c645dbb66d68cc2b03afb8f2eeaa88fbbaPaul Duffin 17312f2d8e2760b78c673b7a187b9062b3938a03147Brian Carlstrom guard.open("close"); 174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 175adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 176e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom @Override protected void finalize() throws IOException { 177e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom try { 17812f2d8e2760b78c673b7a187b9062b3938a03147Brian Carlstrom if (guard != null) { 17912f2d8e2760b78c673b7a187b9062b3938a03147Brian Carlstrom guard.warnIfOpen(); 18012f2d8e2760b78c673b7a187b9062b3938a03147Brian Carlstrom } 181e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom } finally { 182e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom try { 183e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom super.finalize(); 184e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom } catch (Throwable t) { 185e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom throw new AssertionError(t); 186e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom } 187e2f58c9501eac730d048199906dc41fe8e4cd6e9Brian Carlstrom } 188adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 189adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 190adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 191f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * Closes this zip file. This method is idempotent. This method may cause I/O if the 192f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * zip file needs to be deleted. 19357995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IOException 195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an IOException occurs. 196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public void close() throws IOException { 198f7aab022dcbfcd8f27b409ab92b4bca4a84d0b8aBrian Carlstrom guard.close(); 199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 200f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes RandomAccessFile localRaf = raf; 201f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes if (localRaf != null) { // Only close initialized instances 202f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes synchronized (localRaf) { 203f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes raf = null; 204f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes localRaf.close(); 205adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 206f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes if (fileToDeleteOnClose != null) { 207f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes fileToDeleteOnClose.delete(); 208f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes fileToDeleteOnClose = null; 209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 213f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson private void checkNotClosed() { 214f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes if (raf == null) { 21560eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes throw new IllegalStateException("Zip file closed"); 216f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson } 217f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson } 218f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson 219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns an enumeration of the entries. The entries are listed in the 221f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * order in which they appear in the zip file. 22257995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 223f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * <p>If you only need to iterate over the entries in a zip file, and don't 224f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * need random-access entry lookup by name, you should probably use {@link ZipInputStream} 225f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * instead, to avoid paying to construct the in-memory index. 226f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * 227f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * @throws IllegalStateException if this zip file has been closed. 228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public Enumeration<? extends ZipEntry> entries() { 230f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson checkNotClosed(); 231f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes final Iterator<ZipEntry> iterator = entries.values().iterator(); 232f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson 233adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return new Enumeration<ZipEntry>() { 234adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public boolean hasMoreElements() { 235f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson checkNotClosed(); 236f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return iterator.hasNext(); 237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 238adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 239adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public ZipEntry nextElement() { 240f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson checkNotClosed(); 241f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return iterator.next(); 242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project }; 244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 247820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes * Returns this file's comment, or null if it doesn't have one. 248820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes * See {@link ZipOutputStream#setComment}. 249820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes * 250820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes * @throws IllegalStateException if this zip file has been closed. 251820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes * @since 1.7 252820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes */ 253820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes public String getComment() { 254820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes checkNotClosed(); 255820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes return comment; 256820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes } 257820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes 258820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes /** 259f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * Returns the zip entry with the given name, or null if there is no such entry. 26057995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 261f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * @throws IllegalStateException if this zip file has been closed. 262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 263adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public ZipEntry getEntry(String entryName) { 264f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson checkNotClosed(); 265f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (entryName == null) { 26686acc043d3334651ee26c65467d78d6cefedd397Kenny Root throw new NullPointerException("entryName == null"); 267f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 268f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 269f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes ZipEntry ze = entries.get(entryName); 270f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (ze == null) { 271f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes ze = entries.get(entryName + "/"); 272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 273f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return ze; 274adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 277adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns an input stream on the data of the specified {@code ZipEntry}. 27857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @param entry 280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * the ZipEntry. 281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return an input stream of the data contained in the {@code ZipEntry}. 282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @throws IOException 283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * if an {@code IOException} occurs. 284f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * @throws IllegalStateException if this zip file has been closed. 285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public InputStream getInputStream(ZipEntry entry) throws IOException { 287739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes // Make sure this ZipEntry is in this Zip file. We run it through the name lookup. 288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project entry = getEntry(entry.getName()); 289f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (entry == null) { 290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project return null; 291f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 293739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes // Create an InputStream at the right part of the file. 294f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes RandomAccessFile localRaf = raf; 295f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes synchronized (localRaf) { 296f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson // We don't know the entry data's start position. All we have is the 2973d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root // position of the entry's local header. 2986aed748da1deaa820d574446037401893d73be9bWilliam Luh // http://www.pkware.com/documents/casestudies/APPNOTE.TXT 2993d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root RAFStream rafStream = new RAFStream(localRaf, entry.localHeaderRelOffset); 30013f30b9167639ad63da1707102db6320e8f76474Elliott Hughes DataInputStream is = new DataInputStream(rafStream); 3013d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root 3023d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root final int localMagic = Integer.reverseBytes(is.readInt()); 3033d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (localMagic != LOCSIG) { 3043d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throwZipException("Local File Header", localMagic); 3053d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } 3063d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root 3073d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root is.skipBytes(2); 3083d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root 3093d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root // At position 6 we find the General Purpose Bit Flag. 3109edf43dfcc35c761d97eb9156ac4254152ddbc55Elliott Hughes int gpbf = Short.reverseBytes(is.readShort()) & 0xffff; 3117a302a49a7c8b99e2f34fff660e199fb7c776bc1William Luh if ((gpbf & ZipFile.GPBF_UNSUPPORTED_MASK) != 0) { 3126aed748da1deaa820d574446037401893d73be9bWilliam Luh throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf); 3136aed748da1deaa820d574446037401893d73be9bWilliam Luh } 3146aed748da1deaa820d574446037401893d73be9bWilliam Luh 3152da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes // Offset 26 has the file name length, and offset 28 has the extra field length. 3162da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes // These lengths can differ from the ones in the central header. 3172da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes is.skipBytes(18); 3182da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes int fileNameLength = Short.reverseBytes(is.readShort()) & 0xffff; 3192da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes int extraFieldLength = Short.reverseBytes(is.readShort()) & 0xffff; 320739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes is.close(); 321739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes 3222da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes // Skip the variable-size file name and extra field data. 3232da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes rafStream.skip(fileNameLength + extraFieldLength); 3242da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes 3253d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (entry.compressionMethod == ZipEntry.STORED) { 3263d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root rafStream.endOffset = rafStream.offset + entry.size; 3273d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root return rafStream; 3283d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } else { 3293d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root rafStream.endOffset = rafStream.offset + entry.compressedSize; 3302da1bf57a6631f1cbd47cdd7692ba8743c993ad9Elliott Hughes int bufSize = Math.max(1024, (int) Math.min(entry.getSize(), 65535L)); 33113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes return new ZipInflaterInputStream(rafStream, new Inflater(true), bufSize, entry); 332adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 333adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 334adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 335adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 336adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 337adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Gets the file name of this {@code ZipFile}. 33857995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 339adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the file name of this {@code ZipFile}. 340adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public String getName() { 342f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes return filename; 343adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 345adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 346adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Returns the number of {@code ZipEntries} in this {@code ZipFile}. 34757995e8186b54515d5a03bf2ab104c3dc247f1b6Jesse Wilson * 348adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @return the number of entries in this file. 349f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes * @throws IllegalStateException if this zip file has been closed. 350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 351adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public int size() { 352f7f90ec7f63bd80b7724d64ea664fdb589052fceJesse Wilson checkNotClosed(); 353f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes return entries.size(); 354adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 355adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 356f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson /** 357adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Find the central directory and read the contents. 358adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 359f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * <p>The central directory can be followed by a variable-length comment 360adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * field, so we have to scan through it backwards. The comment is at 361adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * most 64K, plus we have 18 bytes for the end-of-central-dir stuff 362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * itself, plus apparently sometimes people throw random junk on the end 363adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * just for the fun of it. 364f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * 365f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * <p>This is all a little wobbly. If the wrong value ends up in the EOCD 366f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * area, we're hosed. This appears to be the way that everybody handles 367f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * it though, so we're in good company if this fails. 368adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 369adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private void readCentralDir() throws IOException { 370f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes // Scan back, looking for the End Of Central Directory field. If the zip file doesn't 371f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes // have an overall comment (unrelated to any per-entry comments), we'll hit the EOCD 372f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes // on the first try. 373f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes // No need to synchronize raf here -- we only do this when we first open the zip file. 374f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes long scanOffset = raf.length() - ENDHDR; 375f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (scanOffset < 0) { 376f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes throw new ZipException("File too short to be a zip file: " + raf.length()); 377f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 378adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 3793d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root raf.seek(0); 3803d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root final int headerMagic = Integer.reverseBytes(raf.readInt()); 381ef164bf196538c04f499dcbb49a389c70ff5601aPaul Duffin if (headerMagic == ENDSIG) { 382ef164bf196538c04f499dcbb49a389c70ff5601aPaul Duffin throw new ZipException("Empty zip archive not supported"); 383ef164bf196538c04f499dcbb49a389c70ff5601aPaul Duffin } 3843d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (headerMagic != LOCSIG) { 3853d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new ZipException("Not a zip archive"); 3863d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } 3873d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root 388f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson long stopOffset = scanOffset - 65536; 389f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (stopOffset < 0) { 390adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project stopOffset = 0; 391f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 392adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 393adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project while (true) { 394f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes raf.seek(scanOffset); 3953d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (Integer.reverseBytes(raf.readInt()) == ENDSIG) { 396adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project break; 397f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 398adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 399adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project scanOffset--; 400f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson if (scanOffset < stopOffset) { 4013d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new ZipException("End Of Central Directory signature not found"); 402f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 403adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 404adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 405820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes // Read the End Of Central Directory. ENDHDR includes the signature bytes, 406820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes // which we've already read. 407820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes byte[] eocd = new byte[ENDHDR - 4]; 408f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes raf.readFully(eocd); 409739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes 41043a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughes // Pull out the information we need. 41143a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughes BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN); 4129f050bd1d16b822532430c897991e27a58605ff5Elliott Hughes int diskNumber = it.readShort() & 0xffff; 4139f050bd1d16b822532430c897991e27a58605ff5Elliott Hughes int diskWithCentralDir = it.readShort() & 0xffff; 4149f050bd1d16b822532430c897991e27a58605ff5Elliott Hughes int numEntries = it.readShort() & 0xffff; 4159f050bd1d16b822532430c897991e27a58605ff5Elliott Hughes int totalNumEntries = it.readShort() & 0xffff; 41643a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughes it.skip(4); // Ignore centralDirSize. 41713f30b9167639ad63da1707102db6320e8f76474Elliott Hughes long centralDirOffset = ((long) it.readInt()) & 0xffffffffL; 418820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes int commentLength = it.readShort() & 0xffff; 419739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes 420739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { 4213d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new ZipException("Spanned archives not supported"); 422f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 423adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 424820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes if (commentLength > 0) { 425820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes byte[] commentBytes = new byte[commentLength]; 426820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes raf.readFully(commentBytes); 42763744c884dd4b4f4307f2b021fb894af164972afElliott Hughes comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8); 428820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes } 429820c09bae53d20c5954d544ebc8a6ee8e54abbaeElliott Hughes 43043a9f774d075e0e441d8b996e3f6c81ea483ec89Elliott Hughes // Seek to the first CDE and read all entries. 43113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes // We have to do this now (from the constructor) rather than lazily because the 43213f30b9167639ad63da1707102db6320e8f76474Elliott Hughes // public API doesn't allow us to throw IOException except from the constructor 43313f30b9167639ad63da1707102db6320e8f76474Elliott Hughes // or from getInputStream. 434f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes RAFStream rafStream = new RAFStream(raf, centralDirOffset); 43513f30b9167639ad63da1707102db6320e8f76474Elliott Hughes BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096); 436739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry. 437739493443ce2ea5b0a92dd1725a4ed630db7b27bElliott Hughes for (int i = 0; i < numEntries; ++i) { 43863744c884dd4b4f4307f2b021fb894af164972afElliott Hughes ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8); 4393d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (newEntry.localHeaderRelOffset >= centralDirOffset) { 4403d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new ZipException("Local file header offset is after central directory"); 4413d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } 44238cad1eb5cc0c30e034063c14c210912d97acb92Geremy Condra String entryName = newEntry.getName(); 44338cad1eb5cc0c30e034063c14c210912d97acb92Geremy Condra if (entries.put(entryName, newEntry) != null) { 44438cad1eb5cc0c30e034063c14c210912d97acb92Geremy Condra throw new ZipException("Duplicate entry name: " + entryName); 44538cad1eb5cc0c30e034063c14c210912d97acb92Geremy Condra } 446adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 447adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 448adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 4493d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root static void throwZipException(String msg, int magic) throws ZipException { 4503d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root final String hexString = IntegralToString.intToHexString(magic, true, 8); 4513d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new ZipException(msg + " signature not found; was " + hexString); 4523d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } 4533d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root 454f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson /** 455f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * Wrap a stream around a RandomAccessFile. The RandomAccessFile is shared 456f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * among all streams returned by getInputStream(), so we have to synchronize 457f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * access to it. (We can optimize this by adding buffering here to reduce 458f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * collisions.) 459adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * 460f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson * <p>We could support mark/reset, but we don't currently need them. 4610c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath * 4620c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath * @hide 463adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 4640c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath public static class RAFStream extends InputStream { 465f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes private final RandomAccessFile sharedRaf; 4663d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root private long endOffset; 467f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes private long offset; 468f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes 4690c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath 4700c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath public RAFStream(RandomAccessFile raf, long initialOffset, long endOffset) { 471f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes sharedRaf = raf; 472f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes offset = initialOffset; 4730c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath this.endOffset = endOffset; 4740c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath } 4750c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath 4760c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException { 4770c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath this(raf, initialOffset, raf.length()); 478adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 479adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 480ff8234c90ecab9f1db368924bf92a5b16460f9b5Elliott Hughes @Override public int available() throws IOException { 4813d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root return (offset < endOffset ? 1 : 0); 482adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 483adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 484ff8234c90ecab9f1db368924bf92a5b16460f9b5Elliott Hughes @Override public int read() throws IOException { 485ff8234c90ecab9f1db368924bf92a5b16460f9b5Elliott Hughes return Streams.readSingleByte(this); 486adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 487adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 488325ff8c68ed5e530e9e1d487b9e2e6d8f8e2bd37Elliott Hughes @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 489f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes synchronized (sharedRaf) { 4903d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root final long length = endOffset - offset; 49134f81a14c7c4002ab141ba90b2498974211b7df2Kenny Root if (byteCount > length) { 49234f81a14c7c4002ab141ba90b2498974211b7df2Kenny Root byteCount = (int) length; 493c2bcd6f029fa02657889af1120f2eaaf73da968eKenny Root } 4943d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root sharedRaf.seek(offset); 495325ff8c68ed5e530e9e1d487b9e2e6d8f8e2bd37Elliott Hughes int count = sharedRaf.read(buffer, byteOffset, byteCount); 496adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (count > 0) { 497f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes offset += count; 498f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return count; 499f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } else { 500f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return -1; 501adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 503adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 504adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 50513f30b9167639ad63da1707102db6320e8f76474Elliott Hughes @Override public long skip(long byteCount) throws IOException { 5063d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (byteCount > endOffset - offset) { 5073d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root byteCount = endOffset - offset; 508f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 509f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes offset += byteCount; 510f9480f317cddcec859025833b748f096247a40aaElliott Hughes return byteCount; 511adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 51213f30b9167639ad63da1707102db6320e8f76474Elliott Hughes 51313f30b9167639ad63da1707102db6320e8f76474Elliott Hughes public int fill(Inflater inflater, int nativeEndBufSize) throws IOException { 514f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes synchronized (sharedRaf) { 5153d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root int len = Math.min((int) (endOffset - offset), nativeEndBufSize); 516f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes int cnt = inflater.setFileInput(sharedRaf.getFD(), offset, nativeEndBufSize); 517f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes // setFileInput read from the file, so we need to get the OS and RAFStream back 518f05aeedc00c8e7ab7650067ce1dc301547a3914bElliott Hughes // in sync... 51913f30b9167639ad63da1707102db6320e8f76474Elliott Hughes skip(cnt); 52013f30b9167639ad63da1707102db6320e8f76474Elliott Hughes return len; 52113f30b9167639ad63da1707102db6320e8f76474Elliott Hughes } 52213f30b9167639ad63da1707102db6320e8f76474Elliott Hughes } 523f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 524f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 5250c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath /** @hide */ 5260c1869ed7f46baf764f9daf4e64c77cd7fbd3516Narayan Kamath public static class ZipInflaterInputStream extends InflaterInputStream { 52713f30b9167639ad63da1707102db6320e8f76474Elliott Hughes private final ZipEntry entry; 52813f30b9167639ad63da1707102db6320e8f76474Elliott Hughes private long bytesRead = 0; 529f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 530f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson public ZipInflaterInputStream(InputStream is, Inflater inf, int bsize, ZipEntry entry) { 531f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson super(is, inf, bsize); 532f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson this.entry = entry; 533f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 534f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 535325ff8c68ed5e530e9e1d487b9e2e6d8f8e2bd37Elliott Hughes @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 5363d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root final int i; 5373d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root try { 5383d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root i = super.read(buffer, byteOffset, byteCount); 5393d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } catch (IOException e) { 5403d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new IOException("Error reading data for " + entry.getName() + " near offset " 5413d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root + bytesRead, e); 5423d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } 5433d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (i == -1) { 5443d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root if (entry.size != bytesRead) { 5453d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root throw new IOException("Size mismatch on inflated file: " + bytesRead + " vs " 5463d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root + entry.size); 5473d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } 5483d2b2ad2cd1f05ba72a550082083da4b5898f30bKenny Root } else { 549f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson bytesRead += i; 550f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 551f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return i; 552f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 553f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson 55413f30b9167639ad63da1707102db6320e8f76474Elliott Hughes @Override public int available() throws IOException { 55560eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes if (closed) { 55660eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes // Our superclass will throw an exception, but there's a jtreg test that 55760eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes // explicitly checks that the InputStream returned from ZipFile.getInputStream 55860eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes // returns 0 even when closed. 55960eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes return 0; 56060eb73fc9f0636c3d0bda5baba641a5f88f8476fElliott Hughes } 561f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson return super.available() == 0 ? 0 : (int) (entry.getSize() - bytesRead); 562f7c6911047d63bc76292f55ce538da32818dd931Jesse Wilson } 563adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 564adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project} 565