< prev index next >

test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java

Print this page

  1 /*
  2  * Copyright (c) 2019, 2021, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 import org.testng.annotations.AfterMethod;
 24 import org.testng.annotations.BeforeMethod;
 25 import org.testng.annotations.Test;
 26 
 27 import java.io.*;








 28 import java.nio.file.Files;
 29 import java.nio.file.Path;
 30 import java.util.List;
 31 import java.util.zip.ZipEntry;
 32 import java.util.zip.ZipFile;
 33 import java.util.zip.ZipOutputStream;
 34 
 35 import static org.testng.Assert.assertTrue;

 36 
 37 /**
 38  * @test
 39  * @bug 8226530
 40  * @summary ZIP File System tests that leverage DirectoryStream

 41  * @compile Zip64SizeTest.java
 42  * @run testng Zip64SizeTest
 43  */
 44 public class Zip64SizeTest {
 45 
 46     private static final int BUFFER_SIZE = 2048;
 47     // ZIP file to create
 48     private static final String ZIP_FILE_NAME = "Zip64SizeTest.zip";
 49     // File that will be created with a size greater than 0xFFFFFFFF
 50     private static final String LARGE_FILE_NAME = "LargeZipEntry.txt";
 51     // File that will be created with a size less than 0xFFFFFFFF
 52     private static final String SMALL_FILE_NAME = "SmallZipEntry.txt";
 53     // List of files to be added to the ZIP file
 54     private static final List<String> ZIP_ENTRIES = List.of(LARGE_FILE_NAME,
 55             SMALL_FILE_NAME);
 56     private static final long LARGE_FILE_SIZE = 5L * 1024L * 1024L * 1024L; // 5GB
 57     private static final long SMALL_FILE_SIZE = 0x100000L; // 1024L x 1024L;
 58 
 59     /**
 60      * Validate that if the size of a ZIP entry exceeds 0xFFFFFFFF, that the
 61      * correct size is returned from the ZIP64 Extended information.
 62      * @throws IOException

 63      */
 64     @Test
 65     private static void validateZipEntrySizes() throws IOException {
 66         createFiles();
 67         createZipFile();
 68         System.out.println("Validating Zip Entry Sizes");
 69         try (ZipFile zip = new ZipFile(ZIP_FILE_NAME)) {
 70             ZipEntry ze = zip.getEntry(LARGE_FILE_NAME);
 71             System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize());
 72             assertTrue(ze.getSize() == LARGE_FILE_SIZE);
 73             ze = zip.getEntry(SMALL_FILE_NAME);
 74             System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize());
 75             assertTrue(ze.getSize() == SMALL_FILE_SIZE);
 76 
 77         }
 78     }
 79 
 80     /**
 81      * Delete the files created for use by the test
 82      * @throws IOException if an error occurs deleting the files




















 83      */
 84     private static void deleteFiles() throws IOException {
 85         Files.deleteIfExists(Path.of(ZIP_FILE_NAME));
 86         Files.deleteIfExists(Path.of(LARGE_FILE_NAME));
 87         Files.deleteIfExists(Path.of(SMALL_FILE_NAME));
























 88     }
 89 
 90     /**
 91      * Create the ZIP file adding an entry whose size exceeds 0xFFFFFFFF
 92      * @throws IOException if an error occurs creating the ZIP File







 93      */
 94     private static void createZipFile() throws IOException {
 95         try (FileOutputStream fos = new FileOutputStream(ZIP_FILE_NAME);
 96              ZipOutputStream zos = new ZipOutputStream(fos)) {
 97             System.out.printf("Creating Zip file: %s%n", ZIP_FILE_NAME);
 98             for (String srcFile : ZIP_ENTRIES) {
 99                 System.out.printf("...Adding Entry: %s%n", srcFile);
100                 File fileToZip = new File(srcFile);
101                 try (FileInputStream fis = new FileInputStream(fileToZip)) {
102                     ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
103                     zipEntry.setSize(fileToZip.length());
104                     zos.putNextEntry(zipEntry);
105                     byte[] bytes = new byte[BUFFER_SIZE];
106                     int length;
107                     while ((length = fis.read(bytes)) >= 0) {
108                         zos.write(bytes, 0, length);
109                     }
110                 }
111             }
112         }
113     }
114 
115     /**
116      * Create the files that will be added to the ZIP file
117      * @throws IOException if there is a problem  creating the files



118      */
119     private static void createFiles() throws IOException {
120         try (RandomAccessFile largeFile = new RandomAccessFile(LARGE_FILE_NAME, "rw");
121              RandomAccessFile smallFile = new RandomAccessFile(SMALL_FILE_NAME, "rw")) {
122             System.out.printf("Creating %s%n", LARGE_FILE_NAME);
123             largeFile.setLength(LARGE_FILE_SIZE);
124             System.out.printf("Creating %s%n", SMALL_FILE_NAME);
125             smallFile.setLength(SMALL_FILE_SIZE);
126         }


127     }
128 
129     /**
130      * Make sure the needed test files do not exist prior to executing the test
131      * @throws IOException
132      */
133     @BeforeMethod
134     public void setUp() throws IOException {
135         deleteFiles();
136     }
137 
138     /**
139      * Remove the files created for the test
140      * @throws IOException
141      */
142     @AfterMethod
143     public void tearDown() throws IOException {
144         deleteFiles();
145     }








146 }

  1 /*
  2  * Copyright (c) 2019, 2024, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */



 23 
 24 import org.junit.jupiter.api.AfterEach;
 25 import org.junit.jupiter.api.BeforeEach;
 26 import org.junit.jupiter.api.Test;
 27 
 28 import java.io.ByteArrayOutputStream;
 29 import java.io.IOException;
 30 import java.nio.ByteBuffer;
 31 import java.nio.ByteOrder;
 32 import java.nio.charset.StandardCharsets;
 33 import java.nio.file.Files;
 34 import java.nio.file.Path;

 35 import java.util.zip.ZipEntry;
 36 import java.util.zip.ZipFile;
 37 import java.util.zip.ZipOutputStream;
 38 
 39 import static org.junit.jupiter.api.Assertions.assertEquals;
 40 
 41 
 42 /**
 43  * @test
 44  * @bug 8226530 8303891
 45  * @summary Verify that ZipFile reads size fields using the Zip64 extra
 46  * field when only the 'uncompressed size' field has the ZIP64 "magic value" 0xFFFFFFFF
 47  * @compile Zip64SizeTest.java
 48  * @run junit Zip64SizeTest
 49  */
 50 public class Zip64SizeTest {


 51     // ZIP file to create
 52     private static final Path ZIP_FILE = Path.of("Zip64SizeTest.zip");
 53     // Contents to write to ZIP entries
 54     private static final byte[] CONTENT = "Hello".getBytes(StandardCharsets.UTF_8);
 55     // This opaque tag will be ignored by ZipEntry.setExtra0
 56     private static final int UNKNOWN_TAG = 0x9902;
 57     // Tag used when converting the extra field to a real ZIP64 extra field
 58     private static final short ZIP64_TAG = 0x1;
 59     // Marker value to indicate that the actual value is stored in the ZIP64 extra field
 60     private static final int ZIP64_MAGIC_VALUE = 0xFFFFFFFF;

 61 
 62     /**
 63      * Validate that if the 'uncompressed size' of a ZIP CEN header is 0xFFFFFFFF, then the
 64      * actual size is retrieved from the corresponding ZIP64 Extended information field.
 65      *
 66      * @throws IOException if an unexpected IOException occurs
 67      */
 68     @Test
 69     public void validateZipEntrySizes() throws IOException {

 70         createZipFile();
 71         System.out.println("Validating Zip Entry Sizes");
 72         try (ZipFile zip = new ZipFile(ZIP_FILE.toFile())) {
 73             ZipEntry ze = zip.getEntry("first");
 74             System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize());
 75             assertEquals(CONTENT.length, ze.getSize());
 76             ze = zip.getEntry("second");
 77             System.out.printf("Entry: %s, size= %s%n", ze.getName(), ze.getSize());
 78             assertEquals(CONTENT.length, ze.getSize());

 79         }
 80     }
 81 
 82     /**
 83      * Create a ZIP file with a CEN entry where the 'uncompressed size' is stored in
 84      * the ZIP64 field, but the 'compressed size' is in the CEN field. This makes the
 85      * ZIP64 data block 8 bytes long, which triggers the regression described in 8226530.
 86      *
 87      * The CEN entry for the "first" entry will have the following structure:
 88      * (Note the CEN 'Uncompressed Length' being 0xFFFFFFFF and the ZIP64
 89      * 'Uncompressed Size' being 5)
 90      *
 91      * 0081 CENTRAL HEADER #1     02014B50
 92      * 0085 Created Zip Spec      14 '2.0'
 93      * 0086 Created OS            00 'MS-DOS'
 94      * [...] Omitted for brevity
 95      * 0091 CRC                   F7D18982
 96      * 0095 Compressed Length     00000007
 97      * 0099 Uncompressed Length   FFFFFFFF
 98      * [...] Omitted for brevity
 99      * 00AF Filename              'first'
100      * 00B4 Extra ID #0001        0001 'ZIP64'
101      * 00B6   Length              0008
102      * 00B8   Uncompressed Size   0000000000000005
103      *
104      * @throws IOException if an error occurs creating the ZIP File
105      */
106     private static void createZipFile() throws IOException {
107         ByteArrayOutputStream baos = new ByteArrayOutputStream();
108         try (ZipOutputStream zos = new ZipOutputStream(baos)) {
109 
110             // The 'first' entry will store 'uncompressed size' in the Zip64 format
111             ZipEntry e1 = new ZipEntry("first");
112 
113             // Make an extra field with the correct size for an 8-byte 'uncompressed size'
114             // Zip64 field. Temporarily use the 'unknown' tag 0x9902 to make
115             // ZipEntry.setExtra0 skip parsing this as a Zip64.
116             // See APPNOTE.TXT, 4.6.1 Third Party Mappings
117             byte[] opaqueExtra = createBlankExtra((short) UNKNOWN_TAG, (short) Long.BYTES);
118             e1.setExtra(opaqueExtra);
119 
120             zos.putNextEntry(e1);
121             zos.write(CONTENT);
122 
123             // A second entry, not in Zip64 format
124             ZipEntry e2 = new ZipEntry("second");
125             zos.putNextEntry(e2);
126             zos.write(CONTENT);
127         }
128 
129         byte[] zip = baos.toByteArray();
130 
131         // Update the CEN of 'first' to use the Zip64 format
132         updateCENHeaderToZip64(zip);
133         Files.write(ZIP_FILE, zip);
134     }
135 
136     /**
137      * Update the CEN entry of the "first" entry to use ZIP64 format for the
138      * 'uncompressed size' field. The updated extra field will have the following
139      * structure:
140      *
141      * 00B4 Extra ID #0001        0001 'ZIP64'
142      * 00B6   Length              0008
143      * 00B8   Uncompressed Size   0000000000000005
144      *
145      * @param zip the ZIP file to update to ZIP64
146      */
147     private static void updateCENHeaderToZip64(byte[] zip) {
148         ByteBuffer buffer = ByteBuffer.wrap(zip).order(ByteOrder.LITTLE_ENDIAN);
149         // Find the offset of the first CEN header
150         int cenOffset = buffer.getInt(zip.length- ZipFile.ENDHDR + ZipFile.ENDOFF);
151         // Find the offset of the extra field
152         int nlen = buffer.getShort(cenOffset + ZipFile.CENNAM);
153         int extraOffset = cenOffset + ZipFile.CENHDR + nlen;
154 
155         // Change the header ID from 'unknown' to ZIP64
156         buffer.putShort(extraOffset, ZIP64_TAG);
157         // Update the 'uncompressed size' ZIP64 value to the actual uncompressed length
158         int fieldOffset = extraOffset
159                 + Short.BYTES // TAG
160                 + Short.BYTES; // data size
161         buffer.putLong(fieldOffset, CONTENT.length);
162 
163         // Set the 'uncompressed size' field of the CEN to 0xFFFFFFFF
164         buffer.putInt(cenOffset + ZipFile.CENLEN, ZIP64_MAGIC_VALUE);

165     }
166 
167     /**
168      * Create an extra field with the given tag and data block size, and a
169      * blank data block.
170      * @return an extra field with the specified tag and size
171      * @param tag the header id of the extra field
172      * @param blockSize the size of the extra field's data block
173      */
174     private static byte[] createBlankExtra(short tag, short blockSize) {
175         int size = Short.BYTES  // tag
176                 + Short.BYTES   // data block size
177                 + blockSize;   // data block;
178 
179         byte[] extra = new byte[size];
180         ByteBuffer.wrap(extra).order(ByteOrder.LITTLE_ENDIAN)
181                 .putShort(0, tag)
182                 .putShort(Short.BYTES, blockSize);
183         return extra;
184     }
185 
186     /**
187      * Make sure the needed test files do not exist prior to executing the test
188      * @throws IOException
189      */
190     @BeforeEach
191     public void setUp() throws IOException {
192         deleteFiles();
193     }
194 
195     /**
196      * Remove the files created for the test
197      * @throws IOException
198      */
199     @AfterEach
200     public void tearDown() throws IOException {
201         deleteFiles();
202     }
203 
204     /**
205      * Delete the files created for use by the test
206      * @throws IOException if an error occurs deleting the files
207      */
208     private static void deleteFiles() throws IOException {
209         Files.deleteIfExists(ZIP_FILE);
210     }
211 }
< prev index next >