View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.commons.compress.archivers.zip;
19  
20  import static org.apache.commons.compress.AbstractTest.getFile;
21  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertNull;
26  import static org.junit.jupiter.api.Assertions.assertThrows;
27  import static org.junit.jupiter.api.Assertions.assertTrue;
28  import static org.junit.jupiter.api.Assumptions.assumeTrue;
29  
30  import java.io.BufferedOutputStream;
31  import java.io.File;
32  import java.io.FileOutputStream;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.RandomAccessFile;
36  import java.nio.file.Files;
37  import java.util.Enumeration;
38  import java.util.Random;
39  import java.util.zip.ZipEntry;
40  
41  import org.apache.commons.compress.AbstractTest;
42  import org.apache.commons.io.RandomAccessFileMode;
43  import org.junit.jupiter.api.Test;
44  
45  public class Zip64SupportIT {
46  
47      interface ZipOutputTest {
48          void test(File f, ZipArchiveOutputStream zos) throws IOException;
49      }
50  
51      private static final long FIVE_BILLION = 5000000000L;
52      private static final int ONE_MILLION = 1000000;
53  
54      private static final int ONE_HUNDRED_THOUSAND = 100000;
55  
56      private static final ZipOutputTest write100KFilesModeNever = (f, zos) -> {
57          zos.setUseZip64(Zip64Mode.Never);
58          final Zip64RequiredException ex = assertThrows(Zip64RequiredException.class, () -> write100KFilesToStream(zos), "expected a Zip64RequiredException");
59          assertEquals(Zip64RequiredException.TOO_MANY_ENTRIES_MESSAGE, ex.getMessage());
60      };
61  
62      private static final ZipOutputTest write3EntriesCreatingBigArchiveModeNever = (f, zos) -> {
63          zos.setUseZip64(Zip64Mode.Never);
64          final Zip64RequiredException ex = assertThrows(Zip64RequiredException.class, () -> write3EntriesCreatingBigArchiveToStream(zos),
65                  "expected a Zip64RequiredException");
66          assertEquals(Zip64RequiredException.ARCHIVE_TOO_BIG_MESSAGE, ex.getMessage());
67      };
68  
69      private static File get100KFileFile() throws Throwable {
70          return getFile("100k_Files.zip");
71      }
72  
73      private static File get100KFileFileGeneratedBy7ZIP() throws Throwable {
74          return getFile("100k_Files_7ZIP.zip");
75      }
76  
77      private static File get100KFileFileGeneratedByJava7Jar() throws Throwable {
78          return getFile("100k_Files_jar.zip");
79      }
80  
81      private static File get100KFileFileGeneratedByPKZip() throws Throwable {
82          return getFile("100k_Files_PKZip.zip");
83      }
84  
85      private static File get100KFileFileGeneratedByWinCF() throws Throwable {
86          return getFile("100k_Files_WindowsCompressedFolders.zip");
87      }
88  
89      private static File get100KFileFileGeneratedByWinZIP() throws Throwable {
90          return getFile("100k_Files_WinZIP.zip");
91      }
92  
93      private static File get5GBZerosFile() throws Throwable {
94          return getFile("5GB_of_Zeros.zip");
95      }
96  
97      private static File get5GBZerosFileGeneratedBy7ZIP() throws Throwable {
98          return getFile("5GB_of_Zeros_7ZIP.zip");
99      }
100 
101     private static File get5GBZerosFileGeneratedByJava7Jar() throws Throwable {
102         return getFile("5GB_of_Zeros_jar.zip");
103     }
104 
105     private static File get5GBZerosFileGeneratedByPKZip() throws Throwable {
106         return getFile("5GB_of_Zeros_PKZip.zip");
107     }
108 
109     private static File get5GBZerosFileGeneratedByWinZIP() throws Throwable {
110         return getFile("5GB_of_Zeros_WinZip.zip");
111     }
112 
113     private static long getLengthAndPositionAtCentralDirectory(final RandomAccessFile a) throws IOException {
114         final long end = a.length();
115         a.seek(end - 22 - 20);
116         final byte[] sig = new byte[4];
117         a.readFully(sig);
118         if (sig[0] != (byte) 0x50 || sig[1] != (byte) 0x4b || sig[2] != 6 || sig[3] != 7) {
119             // not a ZIP64 archive
120             return getLengthAndPositionAtCentralDirectory32(a, end);
121         }
122 
123         final long cdOffsetLoc = end - 22 - 20 - 56 + 48;
124         // seek to central directory locator
125         a.seek(cdOffsetLoc);
126         final byte[] cdOffset = new byte[8];
127         a.readFully(cdOffset);
128         a.seek(ZipEightByteInteger.getLongValue(cdOffset));
129         return end;
130     }
131 
132     private static long getLengthAndPositionAtCentralDirectory32(final RandomAccessFile a, final long end) throws IOException {
133         a.seek(end - 22 + 16);
134         final byte[] cdOffset = new byte[4];
135         a.readFully(cdOffset);
136         a.seek(ZipLong.getValue(cdOffset));
137         return end;
138     }
139 
140     private static File getTempFile(final String testName) throws Throwable {
141         final File f = File.createTempFile("commons-compress-" + testName, ".zip");
142         f.deleteOnExit();
143         return f;
144     }
145 
146     private static void read100KFilesImpl(final File f) throws IOException {
147         try (InputStream fin = Files.newInputStream(f.toPath());
148                 ZipArchiveInputStream zin = new ZipArchiveInputStream(fin)) {
149             int files = 0;
150             ZipArchiveEntry zae;
151             while ((zae = zin.getNextZipEntry()) != null) {
152                 if (!zae.isDirectory()) {
153                     files++;
154                     assertEquals(0, zae.getSize());
155                 }
156             }
157             assertEquals(ONE_HUNDRED_THOUSAND, files);
158         }
159     }
160 
161     private static void read100KFilesUsingZipFileImpl(final File f) throws IOException {
162         ZipFile zf = null;
163         try {
164             zf = ZipFile.builder().setFile(f).get();
165             int files = 0;
166             for (final Enumeration<ZipArchiveEntry> e = zf.getEntries(); e.hasMoreElements();) {
167                 final ZipArchiveEntry zae = e.nextElement();
168                 if (!zae.isDirectory()) {
169                     files++;
170                     assertEquals(0, zae.getSize());
171                 }
172             }
173             assertEquals(ONE_HUNDRED_THOUSAND, files);
174         } finally {
175             ZipFile.closeQuietly(zf);
176         }
177     }
178 
179     private static void read5GBOfZerosImpl(final File f, final String expectedName) throws IOException {
180         try (InputStream fin = Files.newInputStream(f.toPath());
181                 ZipArchiveInputStream zin = new ZipArchiveInputStream(fin)) {
182             ZipArchiveEntry zae = zin.getNextZipEntry();
183             while (zae.isDirectory()) {
184                 zae = zin.getNextZipEntry();
185             }
186             assertEquals(expectedName, zae.getName());
187             final byte[] buf = new byte[1024 * 1024];
188             long read = 0;
189             final Random r = new Random(System.currentTimeMillis());
190             int readNow;
191             while ((readNow = zin.read(buf, 0, buf.length)) > 0) {
192                 // testing all bytes for a value of 0 is going to take
193                 // too long, just pick a few ones randomly
194                 for (int i = 0; i < 1024; i++) {
195                     final int idx = r.nextInt(readNow);
196                     assertEquals(0, buf[idx], "testing byte " + (read + idx));
197                 }
198                 read += readNow;
199             }
200             assertEquals(FIVE_BILLION, read);
201             assertNull(zin.getNextZipEntry());
202             assertEquals(FIVE_BILLION, zae.getSize());
203         }
204     }
205 
206     private static void read5GBOfZerosUsingZipFileImpl(final File f, final String expectedName) throws IOException {
207         ZipFile zf = null;
208         try {
209             zf = ZipFile.builder().setFile(f).get();
210             final Enumeration<ZipArchiveEntry> e = zf.getEntries();
211             assertTrue(e.hasMoreElements());
212             ZipArchiveEntry zae = e.nextElement();
213             while (zae.isDirectory()) {
214                 zae = e.nextElement();
215             }
216             assertEquals(expectedName, zae.getName());
217             assertEquals(FIVE_BILLION, zae.getSize());
218             final byte[] buf = new byte[1024 * 1024];
219             long read = 0;
220             final Random r = new Random(System.currentTimeMillis());
221             int readNow;
222             try (InputStream zin = zf.getInputStream(zae)) {
223                 while ((readNow = zin.read(buf, 0, buf.length)) > 0) {
224                     // testing all bytes for a value of 0 is going to take
225                     // too long, just pick a few ones randomly
226                     for (int i = 0; i < 1024; i++) {
227                         final int idx = r.nextInt(readNow);
228                         assertEquals(0, buf[idx], "testing byte " + (read + idx));
229                     }
230                     read += readNow;
231                 }
232             }
233             assertEquals(FIVE_BILLION, read);
234             assertFalse(e.hasMoreElements());
235         } finally {
236             ZipFile.closeQuietly(zf);
237         }
238     }
239 
240     private static void withTemporaryArchive(final String testName, final ZipOutputTest test, final boolean useRandomAccessFile) throws Throwable {
241         withTemporaryArchive(testName, test, useRandomAccessFile, null);
242     }
243 
244     private static void withTemporaryArchive(final String testName, final ZipOutputTest test, final boolean useRandomAccessFile, final Long splitSize)
245             throws Throwable {
246         File f = getTempFile(testName);
247         File dir = null;
248         if (splitSize != null) {
249             dir = Files.createTempDirectory("commons-compress-" + testName).toFile();
250             dir.deleteOnExit();
251 
252             f = new File(dir, "commons-compress-" + testName + ".zip");
253         }
254         BufferedOutputStream os = null;
255         ZipArchiveOutputStream zos = useRandomAccessFile ? new ZipArchiveOutputStream(f)
256                 : new ZipArchiveOutputStream(os = new BufferedOutputStream(Files.newOutputStream(f.toPath())));
257         if (splitSize != null) {
258             zos = new ZipArchiveOutputStream(f, splitSize);
259         }
260 
261         try {
262             test.test(f, zos);
263         } catch (final IOException ex) {
264             System.err.println("Failed to write archive because of: " + ex.getMessage() + " - likely not enough disk space.");
265             assumeTrue(false);
266         } finally {
267             try {
268                 zos.destroy();
269             } finally {
270                 try {
271                     if (os != null) {
272                         os.close();
273                     }
274                     AbstractTest.forceDelete(f);
275                 } finally {
276                     if (dir != null) {
277                         final File directory = dir;
278                         AbstractTest.forceDelete(directory);
279                     }
280                 }
281             }
282         }
283     }
284 
285     private static ZipOutputTest write100KFiles() {
286         return write100KFiles(Zip64Mode.AsNeeded);
287     }
288 
289     private static ZipOutputTest write100KFiles(final Zip64Mode mode) {
290         return (f, zos) -> {
291             if (mode != Zip64Mode.AsNeeded) {
292                 zos.setUseZip64(mode);
293             }
294             write100KFilesToStream(zos);
295             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
296                 final long end = a.length();
297 
298                 // validate "end of central directory" is at
299                 // the end of the file and contains the magic
300                 // value 0xFFFF as "number of entries".
301                 a.seek(end - 22 /* length of EOCD without file comment */);
302                 final byte[] eocd = new byte[12];
303                 a.readFully(eocd);
304                 assertArrayEquals(new byte[] {
305                         // sig
306                         (byte) 0x50, (byte) 0x4b, 5, 6,
307                         // disk numbers
308                         0, 0, 0, 0,
309                         // entries
310                         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, }, eocd);
311 
312                 // validate "Zip64 end of central directory
313                 // locator" is right in front of the EOCD and
314                 // the location of the "Zip64 end of central
315                 // directory record" seems correct
316                 final long expectedZ64EocdOffset = end - 22 /* eocd.length */
317                         - 20 /* z64 eocd locator.length */
318                         - 56 /* z64 eocd without extensible data sector */;
319                 final byte[] loc = ZipEightByteInteger.getBytes(expectedZ64EocdOffset);
320                 a.seek(end - 22 - 20);
321                 final byte[] z64EocdLoc = new byte[20];
322                 a.readFully(z64EocdLoc);
323                 assertArrayEquals(new byte[] {
324                         // sig
325                         (byte) 0x50, (byte) 0x4b, 6, 7,
326                         // disk numbers
327                         0, 0, 0, 0,
328                         // location of Zip64 EOCD,
329                         loc[0], loc[1], loc[2], loc[3], loc[4], loc[5], loc[6], loc[7],
330                         // total number of disks
331                         1, 0, 0, 0, }, z64EocdLoc);
332 
333                 // validate "Zip64 end of central directory
334                 // record" is where it is supposed to be, the
335                 // known values are fine and read the location
336                 // of the central directory from it
337                 a.seek(expectedZ64EocdOffset);
338                 final byte[] z64EocdStart = new byte[40];
339                 a.readFully(z64EocdStart);
340                 assertArrayEquals(new byte[] {
341                         // sig
342                         (byte) 0x50, (byte) 0x4b, 6, 6,
343                         // size of z64 EOCD
344                         44, 0, 0, 0, 0, 0, 0, 0,
345                         // version made by
346                         45, 0,
347                         // version needed to extract
348                         45, 0,
349                         // disk numbers
350                         0, 0, 0, 0, 0, 0, 0, 0,
351                         // number of entries 100k = 0x186A0
352                         (byte) 0xA0, (byte) 0x86, 1, 0, 0, 0, 0, 0, (byte) 0xA0, (byte) 0x86, 1, 0, 0, 0, 0, 0, }, z64EocdStart);
353                 a.seek(expectedZ64EocdOffset + 48 /* skip size */);
354                 final byte[] cdOffset = new byte[8];
355                 a.readFully(cdOffset);
356                 final long cdLoc = ZipEightByteInteger.getLongValue(cdOffset);
357 
358                 // finally verify there really is a central
359                 // directory entry where the Zip64 EOCD claims
360                 a.seek(cdLoc);
361                 final byte[] sig = new byte[4];
362                 a.readFully(sig);
363                 assertArrayEquals(new byte[] { (byte) 0x50, (byte) 0x4b, 1, 2, }, sig);
364             }
365         };
366     }
367 
368     private static void write100KFilesToStream(final ZipArchiveOutputStream zos) throws IOException {
369         for (int i = 0; i < ONE_HUNDRED_THOUSAND; i++) {
370             final ZipArchiveEntry zae = new ZipArchiveEntry(String.valueOf(i));
371             zae.setSize(0);
372             zos.putArchiveEntry(zae);
373             zos.closeArchiveEntry();
374         }
375         zos.close();
376     }
377 
378     private static ZipOutputTest write3EntriesCreatingBigArchive() {
379         return write3EntriesCreatingBigArchive(Zip64Mode.AsNeeded);
380     }
381 
382     private static ZipOutputTest write3EntriesCreatingBigArchive(final Zip64Mode mode) {
383         return write3EntriesCreatingBigArchive(mode, false);
384     }
385 
386     /*
387      * Individual sizes don't require ZIP64 but the offset of the third entry is bigger than 0xFFFFFFFF so a ZIP64 extended information is needed inside the
388      * central directory.
389      *
390      * Creates a temporary archive of approx 5GB in size
391      */
392     private static ZipOutputTest write3EntriesCreatingBigArchive(final Zip64Mode mode, final boolean isSplitArchive) {
393         return (f, zos) -> {
394             if (mode != Zip64Mode.AsNeeded) {
395                 zos.setUseZip64(mode);
396             }
397             write3EntriesCreatingBigArchiveToStream(zos);
398 
399             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
400                 getLengthAndPositionAtCentralDirectory(a);
401                 // skip first two entries
402                 a.skipBytes(2 * 47 /*
403                                     * CD entry of file with file name length 1 and no extra data
404                                     */
405                         + 2 * (mode == Zip64Mode.Always ? 32 : 0)
406                 /* ZIP64 extra fields if mode is Always */
407                 );
408 
409                 // grab third entry, verify offset is
410                 // 0xFFFFFFFF, and it has a ZIP64 extended
411                 // information extra field
412                 final byte[] header = new byte[12];
413                 a.readFully(header);
414                 assertArrayEquals(new byte[] {
415                         // sig
416                         (byte) 0x50, (byte) 0x4b, 1, 2,
417                         // version made by
418                         45, 0,
419                         // version needed to extract
420                         45, 0,
421                         // GPB (EFS bit)
422                         0, 8,
423                         // method
424                         0, 0 }, header, "CDH start");
425                 // ignore timestamp, CRC, compressed size
426                 a.skipBytes(12);
427                 // Original Size
428                 final byte[] originalSize = new byte[4];
429                 a.readFully(originalSize);
430                 if (mode == Zip64Mode.Always) {
431                     assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, }, originalSize, "CDH original size");
432                 } else {
433                     assertArrayEquals(new byte[] { 1, 0, 0, 0 }, originalSize, "CDH original size");
434                 }
435                 final byte[] rest = new byte[19];
436                 a.readFully(rest);
437                 assertArrayEquals(new byte[] {
438                         // file name length
439                         1, 0,
440                         // extra field length
441                         (byte) (mode == Zip64Mode.Always ? 32 : 12), 0,
442                         // comment length
443                         0, 0,
444                         // disk number
445                         (byte) (isSplitArchive ? 0xFF : 0), (byte) (isSplitArchive ? 0xFF : 0),
446                         // attributes
447                         0, 0, 0, 0, 0, 0,
448                         // offset
449                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
450                         // file name
451                         (byte) '2' }, rest, "CDH rest");
452                 if (mode == Zip64Mode.Always) {
453                     final byte[] extra1 = new byte[12];
454                     a.readFully(extra1);
455                     assertArrayEquals(new byte[] {
456                             // Header-ID
457                             1, 0,
458                             // size
459                             28, 0,
460                             // Original Size
461                             1, 0, 0, 0, 0, 0, 0, 0, }, extra1, "CDH extra");
462                     // skip compressed size
463                     a.skipBytes(8);
464                 } else {
465                     final byte[] extra2 = new byte[4];
466                     a.readFully(extra2);
467                     assertArrayEquals(new byte[] {
468                             // Header-ID
469                             1, 0,
470                             // size
471                             8, 0, }, extra2, "CDH extra");
472                 }
473 
474                 // read offset of LFH
475                 final byte[] offset = new byte[8];
476                 a.readFully(offset);
477                 // verify there is an LFH where the CD claims it
478                 a.seek(ZipEightByteInteger.getLongValue(offset));
479                 final byte[] sig = new byte[4];
480                 a.readFully(sig);
481                 assertArrayEquals(new byte[] { (byte) 0x50, (byte) 0x4b, 3, 4, }, sig, "LFH signature");
482             }
483         };
484     }
485 
486     private static void write3EntriesCreatingBigArchiveToStream(final ZipArchiveOutputStream zos) throws IOException {
487         final byte[] buf = new byte[ONE_MILLION];
488         ZipArchiveEntry zae;
489         for (int i = 0; i < 2; i++) {
490             zae = new ZipArchiveEntry(String.valueOf(i));
491             zae.setSize(FIVE_BILLION / 2);
492             zae.setMethod(ZipEntry.STORED);
493             zae.setCrc(0x8a408f16L);
494             zos.putArchiveEntry(zae);
495             for (int j = 0; j < FIVE_BILLION / 2 / 1000 / 1000; j++) {
496                 zos.write(buf);
497             }
498             zos.closeArchiveEntry();
499         }
500         zae = new ZipArchiveEntry(String.valueOf(2));
501         zae.setSize(1);
502         zae.setMethod(ZipEntry.STORED);
503         zae.setCrc(0x9b9265bL);
504         zos.putArchiveEntry(zae);
505         zos.write(new byte[] { 42 });
506         zos.closeArchiveEntry();
507         zos.close();
508     }
509 
510     private static File write5GBZerosFile(final String testName) throws Throwable {
511         final File f = getTempFile(testName);
512         final ZipArchiveOutputStream zos = new ZipArchiveOutputStream(f);
513         try {
514             zos.setUseZip64(Zip64Mode.Always);
515             final byte[] buf = new byte[ONE_MILLION];
516             final ZipArchiveEntry zae = new ZipArchiveEntry("5GB_of_Zeros");
517             zae.setSize(FIVE_BILLION);
518             zae.setMethod(ZipEntry.DEFLATED);
519             zae.setCrc(0x8a408f16L);
520             zos.putArchiveEntry(zae);
521             for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
522                 zos.write(buf);
523             }
524             zos.closeArchiveEntry();
525             zos.close();
526         } catch (final IOException ex) {
527             System.err.println("Failed to write archive because of: " + ex.getMessage() + " - likely not enough disk space.");
528             assumeTrue(false);
529         } finally {
530             zos.destroy();
531         }
532         return f;
533     }
534 
535     private static ZipOutputTest writeBigDeflatedEntryToFile(final boolean knownSize) {
536         return writeBigDeflatedEntryToFile(knownSize, Zip64Mode.AsNeeded);
537     }
538 
539     /*
540      * One entry of length 5 billion bytes, written with compression to a file.
541      *
542      * Writing to a file => sizes are stored directly inside the LFH. No Data Descriptor at all.
543      *
544      * Creates a temporary archive of approx 4MB in size
545      */
546     private static ZipOutputTest writeBigDeflatedEntryToFile(final boolean knownSize, final Zip64Mode mode) {
547         return (f, zos) -> {
548             if (mode != Zip64Mode.AsNeeded) {
549                 zos.setUseZip64(mode);
550             }
551             final byte[] buf = new byte[ONE_MILLION];
552             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
553             if (knownSize) {
554                 zae.setSize(FIVE_BILLION);
555             }
556             zae.setMethod(ZipEntry.DEFLATED);
557             zos.putArchiveEntry(zae);
558             for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
559                 zos.write(buf);
560             }
561             zos.closeArchiveEntry();
562             zos.close();
563 
564             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
565                 getLengthAndPositionAtCentralDirectory(a);
566 
567                 // grab first entry, verify
568                 // sizes are 0xFFFFFFFF and
569                 // it has a ZIP64 extended
570                 // information extra field
571                 byte[] header = new byte[12];
572                 a.readFully(header);
573                 assertArrayEquals(new byte[] {
574                         // sig
575                         (byte) 0x50, (byte) 0x4b, 1, 2,
576                         // version made by
577                         45, 0,
578                         // version needed to extract
579                         45, 0,
580                         // GPB (EFS + *no* Data Descriptor)
581                         0, 8,
582                         // method
583                         8, 0, }, header, "CDH start");
584                 // ignore timestamp
585                 a.skipBytes(4);
586                 byte[] rest = new byte[26];
587                 a.readFully(rest);
588                 assertArrayEquals(new byte[] {
589                         // CRC
590                         (byte) 0x50, (byte) 0x6F, (byte) 0x31, (byte) 0x5c,
591                         // Compressed Size
592                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
593                         // Original Size
594                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
595                         // file name length
596                         1, 0,
597                         // extra field length
598                         (byte) (mode == Zip64Mode.Always ? 32 : 20), 0,
599                         // comment length
600                         0, 0,
601                         // disk number
602                         0, 0,
603                         // attributes
604                         0, 0, 0, 0, 0, 0, }, rest, "CDH rest");
605                 byte[] offset = new byte[4];
606                 a.readFully(offset);
607                 if (mode == Zip64Mode.Always) {
608                     assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, }, offset, "offset");
609                 } else {
610                     assertArrayEquals(new byte[] { 0, 0, 0, 0, }, offset, "offset");
611                 }
612                 assertEquals('0', a.read());
613                 byte[] extra = new byte[12];
614                 a.readFully(extra);
615                 // 5e9 == 0x12A05F200
616                 assertArrayEquals(new byte[] {
617                         // Header-ID
618                         1, 0,
619                         // size of extra
620                         (byte) (mode == Zip64Mode.Always ? 28 : 16), 0,
621                         // original size
622                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0, }, extra, "CDH extra");
623                 if (mode == Zip64Mode.Always) {
624                     // skip compressed size
625                     a.skipBytes(8);
626                     offset = new byte[8];
627                     a.readFully(offset);
628                     assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, }, offset, "extra offset");
629                 }
630 
631                 // and now validate local file header
632                 a.seek(0);
633                 header = new byte[10];
634                 a.readFully(header);
635                 assertArrayEquals(new byte[] {
636                         // sig
637                         (byte) 0x50, (byte) 0x4b, 3, 4,
638                         // version needed to extract
639                         45, 0,
640                         // GPB (EFS bit, no DD)
641                         0, 8,
642                         // method
643                         8, 0, }, header, "LFH start");
644                 // ignore timestamp
645                 a.skipBytes(4);
646                 rest = new byte[17];
647                 a.readFully(rest);
648                 assertArrayEquals(new byte[] {
649                         // CRC
650                         (byte) 0x50, (byte) 0x6F, (byte) 0x31, (byte) 0x5c,
651                         // Compressed Size
652                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
653                         // Original Size
654                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
655                         // file name length
656                         1, 0,
657                         // extra field length
658                         20, 0,
659                         // file name
660                         (byte) '0' }, rest);
661                 extra = new byte[12];
662                 a.readFully(extra);
663                 assertArrayEquals(new byte[] {
664                         // Header-ID
665                         1, 0,
666                         // size of extra
667                         16, 0,
668                         // original size
669                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0,
670                         // skip compressed size
671                 }, extra);
672             }
673         };
674     }
675 
676     /*
677      * One entry of length 5 billion bytes, written with compression to a file.
678      *
679      * Writing to a file => sizes are stored directly inside the LFH. No Data Descriptor at all.
680      *
681      * Creates a temporary archive of approx 4MB in size
682      */
683     private static ZipOutputTest writeBigDeflatedEntryToFileModeNever(final boolean knownSize) {
684         return (f, zos) -> {
685             zos.setUseZip64(Zip64Mode.Never);
686             final Zip64RequiredException ex = assertThrows(Zip64RequiredException.class, () -> {
687                 final byte[] buf = new byte[ONE_MILLION];
688                 final ZipArchiveEntry zae = new ZipArchiveEntry("0");
689                 if (knownSize) {
690                     zae.setSize(FIVE_BILLION);
691                 }
692                 zae.setMethod(ZipEntry.DEFLATED);
693                 zos.putArchiveEntry(zae);
694                 for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
695                     zos.write(buf);
696                 }
697                 zos.closeArchiveEntry();
698             }, "expected a Zip64RequiredException");
699             assertTrue(ex.getMessage().startsWith("0's size"));
700         };
701     }
702 
703     /*
704      * One entry of length 5 billion bytes, written with compression to a stream.
705      *
706      * Compression + Stream => sizes are set to 0 in LFH and ZIP64 entry, real values are inside the data descriptor.
707      *
708      * Creates a temporary archive of approx 4MB in size
709      */
710     private static ZipOutputTest writeBigDeflatedEntryToStream(final boolean knownSize, final Zip64Mode mode) {
711         return (f, zos) -> {
712             if (mode != Zip64Mode.AsNeeded) {
713                 zos.setUseZip64(mode);
714             }
715             final byte[] buf = new byte[ONE_MILLION];
716             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
717             if (knownSize) {
718                 zae.setSize(FIVE_BILLION);
719             }
720             zae.setMethod(ZipEntry.DEFLATED);
721             zos.putArchiveEntry(zae);
722             for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
723                 zos.write(buf);
724             }
725             zos.closeArchiveEntry();
726             zos.close();
727 
728             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
729                 getLengthAndPositionAtCentralDirectory(a);
730 
731                 final long cfhPos = a.getFilePointer();
732                 // grab first entry, verify
733                 // sizes are 0xFFFFFFFF and
734                 // it has a ZIP64 extended
735                 // information extra field
736                 byte[] header = new byte[12];
737                 a.readFully(header);
738                 assertArrayEquals(new byte[] {
739                         // sig
740                         (byte) 0x50, (byte) 0x4b, 1, 2,
741                         // version made by
742                         45, 0,
743                         // version needed to extract
744                         45, 0,
745                         // GPB (EFS + Data Descriptor)
746                         8, 8,
747                         // method
748                         8, 0, }, header, "CDH start");
749                 // ignore timestamp
750                 a.skipBytes(4);
751                 byte[] rest = new byte[26];
752                 a.readFully(rest);
753                 assertArrayEquals(new byte[] {
754                         // CRC
755                         (byte) 0x50, (byte) 0x6F, (byte) 0x31, (byte) 0x5c,
756                         // Compressed Size
757                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
758                         // Original Size
759                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
760                         // file name length
761                         1, 0,
762                         // extra field length
763                         (byte) (mode == Zip64Mode.Always ? 32 : 20), 0,
764                         // comment length
765                         0, 0,
766                         // disk number
767                         0, 0,
768                         // attributes
769                         0, 0, 0, 0, 0, 0, }, rest, "CDH rest");
770                 byte[] offset = new byte[4];
771                 a.readFully(offset);
772                 if (mode == Zip64Mode.Always) {
773                     assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, }, offset, "offset");
774                 } else {
775                     assertArrayEquals(new byte[] { 0, 0, 0, 0, }, offset, "offset");
776                 }
777                 assertEquals('0', a.read());
778                 byte[] extra = new byte[12];
779                 a.readFully(extra);
780                 // 5e9 == 0x12A05F200
781                 assertArrayEquals(new byte[] {
782                         // Header-ID
783                         1, 0,
784                         // size of extra
785                         (byte) (mode == Zip64Mode.Always ? 28 : 16), 0,
786                         // original size
787                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0, }, extra, "CDH extra");
788                 if (mode == Zip64Mode.Always) {
789                     // skip compressed size
790                     a.skipBytes(8);
791                     offset = new byte[8];
792                     a.readFully(offset);
793                     assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, }, offset, "extra offset");
794                 }
795 
796                 // validate data descriptor
797                 a.seek(cfhPos - 24);
798                 byte[] dd = new byte[8];
799                 a.readFully(dd);
800                 assertArrayEquals(new byte[] {
801                         // sig
802                         (byte) 0x50, (byte) 0x4b, 7, 8,
803                         // CRC
804                         (byte) 0x50, (byte) 0x6F, (byte) 0x31, (byte) 0x5c, }, dd, "DD");
805                 // skip compressed size
806                 a.skipBytes(8);
807                 dd = new byte[8];
808                 a.readFully(dd);
809                 assertArrayEquals(new byte[] {
810                         // original size
811                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0, }, dd, "DD sizes");
812 
813                 // and now validate local file header
814                 a.seek(0);
815                 header = new byte[10];
816                 a.readFully(header);
817                 assertArrayEquals(new byte[] {
818                         // sig
819                         (byte) 0x50, (byte) 0x4b, 3, 4,
820                         // version needed to extract
821                         45, 0,
822                         // GPB (EFS + Data Descriptor)
823                         8, 8,
824                         // method
825                         8, 0, }, header, "LFH start");
826                 // ignore timestamp
827                 a.skipBytes(4);
828                 rest = new byte[17];
829                 a.readFully(rest);
830                 assertArrayEquals(new byte[] {
831                         // CRC
832                         0, 0, 0, 0,
833                         // Compressed Size
834                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
835                         // Original Size
836                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
837                         // file name length
838                         1, 0,
839                         // extra field length
840                         20, 0,
841                         // file name
842                         (byte) '0' }, rest, "LFH rest");
843                 extra = new byte[20];
844                 a.readFully(extra);
845                 assertArrayEquals(new byte[] {
846                         // Header-ID
847                         1, 0,
848                         // size of extra
849                         16, 0,
850                         // original size
851                         0, 0, 0, 0, 0, 0, 0, 0,
852                         // compressed size
853                         0, 0, 0, 0, 0, 0, 0, 0, }, extra, "LFH extra");
854             }
855         };
856     }
857 
858     private static ZipOutputTest writeBigDeflatedEntryUnknownSizeToStream(final Zip64Mode mode) {
859         return (f, zos) -> {
860             final Zip64RequiredException ex = assertThrows(Zip64RequiredException.class, () -> {
861                 if (mode != Zip64Mode.AsNeeded) {
862                     zos.setUseZip64(mode);
863                 }
864                 final byte[] buf = new byte[ONE_MILLION];
865                 final ZipArchiveEntry zae = new ZipArchiveEntry("0");
866                 zae.setMethod(ZipEntry.DEFLATED);
867                 zos.putArchiveEntry(zae);
868                 for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
869                     zos.write(buf);
870                 }
871                 zos.closeArchiveEntry();
872             }, "expected a Zip64RequiredException");
873             assertTrue(ex.getMessage().startsWith("0's size"));
874         };
875     }
876 
877     private static ZipOutputTest writeBigStoredEntry(final boolean knownSize) {
878         return writeBigStoredEntry(knownSize, Zip64Mode.AsNeeded);
879     }
880 
881     /*
882      * One entry of length 5 billion bytes, written without compression.
883      *
884      * No Compression => sizes are stored directly inside the LFH. No Data Descriptor at all.
885      *
886      * Creates a temporary archive of approx 5GB in size
887      */
888     private static ZipOutputTest writeBigStoredEntry(final boolean knownSize, final Zip64Mode mode) {
889         return (f, zos) -> {
890             if (mode != Zip64Mode.AsNeeded) {
891                 zos.setUseZip64(mode);
892             }
893             final byte[] buf = new byte[ONE_MILLION];
894             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
895             if (knownSize) {
896                 zae.setSize(FIVE_BILLION);
897                 zae.setCrc(0x5c316f50L);
898             }
899             zae.setMethod(ZipEntry.STORED);
900             zos.putArchiveEntry(zae);
901             for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
902                 zos.write(buf);
903             }
904             zos.closeArchiveEntry();
905             zos.close();
906 
907             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
908                 getLengthAndPositionAtCentralDirectory(a);
909 
910                 // grab first entry, verify sizes are 0xFFFFFFFF
911                 // and it has a ZIP64 extended information extra
912                 // field
913                 byte[] header = new byte[12];
914                 a.readFully(header);
915                 assertArrayEquals(new byte[] {
916                         // sig
917                         (byte) 0x50, (byte) 0x4b, 1, 2,
918                         // version made by
919                         45, 0,
920                         // version needed to extract
921                         45, 0,
922                         // GPB (EFS bit)
923                         0, 8,
924                         // method
925                         0, 0 }, header, "CDH start");
926                 // ignore timestamp
927                 a.skipBytes(4);
928                 byte[] rest = new byte[26];
929                 a.readFully(rest);
930                 assertArrayEquals(new byte[] {
931                         // CRC
932                         (byte) 0x50, (byte) 0x6F, (byte) 0x31, (byte) 0x5c,
933                         // Compressed Size
934                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
935                         // Original Size
936                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
937                         // file name length
938                         1, 0,
939                         // extra field length
940                         (byte) (mode == Zip64Mode.Always ? 32 : 20), 0,
941                         // comment length
942                         0, 0,
943                         // disk number
944                         0, 0,
945                         // attributes
946                         0, 0, 0, 0, 0, 0, }, rest, "CDH rest");
947                 byte[] offset = new byte[4];
948                 a.readFully(offset);
949                 if (mode == Zip64Mode.Always) {
950                     assertArrayEquals(new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, }, offset, "offset");
951                 } else {
952                     assertArrayEquals(new byte[] { 0, 0, 0, 0, }, offset, "offset");
953                 }
954                 assertEquals('0', a.read());
955                 final byte[] extra = new byte[20];
956                 a.readFully(extra);
957                 // 5e9 == 0x12A05F200
958                 assertArrayEquals(new byte[] {
959                         // Header-ID
960                         1, 0,
961                         // size of extra
962                         (byte) (mode == Zip64Mode.Always ? 28 : 16), 0,
963                         // original size
964                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0,
965                         // compressed size
966                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0, }, extra, "CDH extra");
967                 if (mode == Zip64Mode.Always) {
968                     offset = new byte[8];
969                     a.readFully(offset);
970                     assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, }, offset, "extra offset");
971                 }
972 
973                 // and now validate local file header
974                 a.seek(0);
975                 header = new byte[10];
976                 a.readFully(header);
977                 assertArrayEquals(new byte[] {
978                         // sig
979                         (byte) 0x50, (byte) 0x4b, 3, 4,
980                         // version needed to extract
981                         45, 0,
982                         // GPB (EFS bit)
983                         0, 8,
984                         // method
985                         0, 0 }, header, "LFH start");
986                 // ignore timestamp
987                 a.skipBytes(4);
988                 rest = new byte[17];
989                 a.readFully(rest);
990                 assertArrayEquals(new byte[] {
991                         // CRC
992                         (byte) 0x50, (byte) 0x6F, (byte) 0x31, (byte) 0x5c,
993                         // Compressed Size
994                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
995                         // Original Size
996                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
997                         // file name length
998                         1, 0,
999                         // extra field length
1000                         20, 0,
1001                         // file name
1002                         (byte) '0' }, rest, "LFH rest");
1003                 a.readFully(extra);
1004                 // 5e9 == 0x12A05F200
1005                 assertArrayEquals(new byte[] {
1006                         // Header-ID
1007                         1, 0,
1008                         // size of extra
1009                         16, 0,
1010                         // original size
1011                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0,
1012                         // compressed size
1013                         0, (byte) 0xF2, 5, (byte) 0x2A, 1, 0, 0, 0, }, extra, "LFH extra");
1014             }
1015         };
1016     }
1017 
1018     private static ZipOutputTest writeBigStoredEntryModeNever(final boolean knownSize) {
1019         return (f, zos) -> {
1020             zos.setUseZip64(Zip64Mode.Never);
1021             final Zip64RequiredException ex = assertThrows(Zip64RequiredException.class, () -> {
1022                 final byte[] buf = new byte[ONE_MILLION];
1023                 final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1024                 if (knownSize) {
1025                     zae.setSize(FIVE_BILLION);
1026                     zae.setCrc(0x5c316f50L);
1027                 }
1028                 zae.setMethod(ZipEntry.STORED);
1029                 zos.putArchiveEntry(zae);
1030                 for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
1031                     zos.write(buf);
1032                 }
1033                 zos.closeArchiveEntry();
1034             }, "expected a Zip64RequiredException");
1035             assertTrue(ex.getMessage().startsWith("0's size"));
1036         };
1037     }
1038 
1039     private static ZipOutputTest writeSmallDeflatedEntryToFile(final boolean knownSize) {
1040         return writeSmallDeflatedEntryToFile(knownSize, Zip64Mode.AsNeeded);
1041     }
1042 
1043     /*
1044      * One entry of length 1 million bytes, written with compression to a file.
1045      *
1046      * Writing to a file => sizes are stored directly inside the LFH. No Data Descriptor at all. Shouldn't contain any ZIP64 extra field if size was known.
1047      */
1048     private static ZipOutputTest writeSmallDeflatedEntryToFile(final boolean knownSize, final Zip64Mode mode) {
1049         return (f, zos) -> {
1050             if (mode != Zip64Mode.AsNeeded) {
1051                 zos.setUseZip64(mode);
1052             }
1053             final byte[] buf = new byte[ONE_MILLION];
1054             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1055             if (knownSize) {
1056                 zae.setSize(ONE_MILLION);
1057             }
1058             zae.setMethod(ZipEntry.DEFLATED);
1059             zos.putArchiveEntry(zae);
1060             zos.write(buf);
1061             zos.closeArchiveEntry();
1062             zos.close();
1063 
1064             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
1065                 getLengthAndPositionAtCentralDirectory(a);
1066 
1067                 // grab first CD entry, verify sizes are not
1068                 // 0xFFFFFFFF and it has a no ZIP64 extended
1069                 // information extra field
1070                 byte[] header = new byte[12];
1071                 a.readFully(header);
1072                 assertArrayEquals(new byte[] {
1073                         // sig
1074                         (byte) 0x50, (byte) 0x4b, 1, 2,
1075                         // version made by
1076                         20, 0,
1077                         // version needed to extract
1078                         20, 0,
1079                         // GPB (EFS + *no* Data Descriptor)
1080                         0, 8,
1081                         // method
1082                         8, 0, }, header);
1083                 // ignore timestamp
1084                 a.skipBytes(4);
1085                 byte[] crc = new byte[4];
1086                 a.readFully(crc);
1087                 assertArrayEquals(new byte[] { (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, crc);
1088                 // skip compressed size
1089                 a.skipBytes(4);
1090                 byte[] rest = new byte[23];
1091                 a.readFully(rest);
1092                 assertArrayEquals(new byte[] {
1093                         // Original Size
1094                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1095                         // file name length
1096                         1, 0,
1097                         // extra field length
1098                         0, 0,
1099                         // comment length
1100                         0, 0,
1101                         // disk number
1102                         0, 0,
1103                         // attributes
1104                         0, 0, 0, 0, 0, 0,
1105                         // offset
1106                         0, 0, 0, 0,
1107                         // file name
1108                         (byte) '0' }, rest);
1109 
1110                 // and now validate local file header
1111                 a.seek(0);
1112                 header = new byte[10];
1113                 a.readFully(header);
1114                 assertArrayEquals(new byte[] {
1115                         // sig
1116                         (byte) 0x50, (byte) 0x4b, 3, 4,
1117                         // version needed to extract
1118                         20, 0,
1119                         // GPB (EFS bit, no DD)
1120                         0, 8,
1121                         // method
1122                         8, 0, }, header);
1123                 // ignore timestamp
1124                 a.skipBytes(4);
1125                 crc = new byte[4];
1126                 a.readFully(crc);
1127                 assertArrayEquals(new byte[] { (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, crc);
1128                 // skip compressed size
1129                 a.skipBytes(4);
1130                 rest = new byte[9];
1131                 a.readFully(rest);
1132 
1133                 final boolean hasExtra = mode == Zip64Mode.AsNeeded && !knownSize;
1134 
1135                 assertArrayEquals(new byte[] {
1136                         // Original Size
1137                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1138                         // file name length
1139                         1, 0,
1140                         // extra field length
1141                         (byte) (!hasExtra ? 0 : 20), 0,
1142                         // file name
1143                         (byte) '0' }, rest);
1144                 if (hasExtra) {
1145                     final byte[] extra = new byte[12];
1146                     a.readFully(extra);
1147                     assertArrayEquals(new byte[] {
1148                             // Header-ID
1149                             1, 0,
1150                             // size of extra
1151                             16, 0,
1152                             // original size
1153                             (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0,
1154                             // don't know the
1155                             // compressed size,
1156                             // don't want to
1157                             // hard-code it
1158                     }, extra);
1159                 }
1160             }
1161         };
1162     }
1163 
1164     /*
1165      * One entry of length 1 million bytes, written with compression to a file.
1166      *
1167      * Writing to a file => sizes are stored directly inside the LFH. No Data Descriptor at all. Must contain ZIP64 extra field as mode is Always.
1168      */
1169     private static ZipOutputTest writeSmallDeflatedEntryToFileModeAlways(final boolean knownSize) {
1170         return (f, zos) -> {
1171             zos.setUseZip64(Zip64Mode.Always);
1172             final byte[] buf = new byte[ONE_MILLION];
1173             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1174             if (knownSize) {
1175                 zae.setSize(ONE_MILLION);
1176             }
1177             zae.setMethod(ZipEntry.DEFLATED);
1178             zos.putArchiveEntry(zae);
1179             zos.write(buf);
1180             zos.closeArchiveEntry();
1181             zos.close();
1182 
1183             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
1184                 getLengthAndPositionAtCentralDirectory(a);
1185 
1186                 // grab first CD entry, verify sizes are not
1187                 // 0xFFFFFFFF, and it has an empty ZIP64 extended
1188                 // information extra field
1189                 byte[] header = new byte[12];
1190                 a.readFully(header);
1191                 assertArrayEquals(new byte[] {
1192                         // sig
1193                         (byte) 0x50, (byte) 0x4b, 1, 2,
1194                         // version made by
1195                         45, 0,
1196                         // version needed to extract
1197                         45, 0,
1198                         // GPB (EFS + *no* Data Descriptor)
1199                         0, 8,
1200                         // method
1201                         8, 0, }, header, "CDH start");
1202                 // ignore timestamp
1203                 a.skipBytes(4);
1204                 byte[] crc = new byte[4];
1205                 a.readFully(crc);
1206                 assertArrayEquals(new byte[] { (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, crc, "CDH CRC");
1207                 // skip compressed size
1208                 a.skipBytes(4);
1209                 byte[] rest = new byte[23];
1210                 a.readFully(rest);
1211                 assertArrayEquals(new byte[] {
1212                         // Original Size
1213                         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
1214                         // file name length
1215                         1, 0,
1216                         // extra field length
1217                         32, 0,
1218                         // comment length
1219                         0, 0,
1220                         // disk number
1221                         0, 0,
1222                         // attributes
1223                         0, 0, 0, 0, 0, 0,
1224                         // offset
1225                         (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
1226                         // file name
1227                         (byte) '0' }, rest, "CDH rest");
1228                 byte[] extra = new byte[12];
1229                 a.readFully(extra);
1230                 assertArrayEquals(new byte[] {
1231                         // Header-ID
1232                         1, 0,
1233                         // size of extra
1234                         28, 0,
1235                         // original size
1236                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0, }, extra, "CDH extra");
1237                 // skip compressed size
1238                 a.skipBytes(8);
1239                 final byte[] offset = new byte[8];
1240                 a.readFully(offset);
1241                 assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, }, offset, "extra offset");
1242 
1243                 // and now validate local file header
1244                 a.seek(0);
1245                 header = new byte[10];
1246                 a.readFully(header);
1247                 assertArrayEquals(new byte[] {
1248                         // sig
1249                         (byte) 0x50, (byte) 0x4b, 3, 4,
1250                         // version needed to extract
1251                         45, 0,
1252                         // GPB (EFS bit, no DD)
1253                         0, 8,
1254                         // method
1255                         8, 0, }, header, "LFH start");
1256                 // ignore timestamp
1257                 a.skipBytes(4);
1258                 crc = new byte[4];
1259                 a.readFully(crc);
1260                 assertArrayEquals(new byte[] { (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, crc, "LFH CRC");
1261                 rest = new byte[13];
1262                 a.readFully(rest);
1263 
1264                 assertArrayEquals(new byte[] {
1265                         // Compressed Size
1266                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1267                         // Original Size
1268                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1269                         // file name length
1270                         1, 0,
1271                         // extra field length
1272                         20, 0,
1273                         // file name
1274                         (byte) '0' }, rest, "LFH rest");
1275 
1276                 extra = new byte[12];
1277                 a.readFully(extra);
1278                 assertArrayEquals(new byte[] {
1279                         // Header-ID
1280                         1, 0,
1281                         // size of extra
1282                         16, 0,
1283                         // original size
1284                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0,
1285                         // don't know the
1286                         // compressed size,
1287                         // don't want to
1288                         // hard-code it
1289                 }, extra, "LFH extra");
1290             }
1291         };
1292     }
1293 
1294     /*
1295      * One entry of length 1 million bytes, written with compression to a stream.
1296      *
1297      * Compression + Stream => sizes are set to 0 in LFH, real values are inside the data descriptor. No ZIP64 extra field at all.
1298      */
1299     private static ZipOutputTest writeSmallDeflatedEntryToStream(final boolean knownSize, final Zip64Mode mode) {
1300         return (f, zos) -> {
1301             if (mode != Zip64Mode.AsNeeded) {
1302                 zos.setUseZip64(mode);
1303             }
1304             final byte[] buf = new byte[ONE_MILLION];
1305             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1306             if (knownSize) {
1307                 zae.setSize(ONE_MILLION);
1308             }
1309             zae.setMethod(ZipEntry.DEFLATED);
1310             zos.putArchiveEntry(zae);
1311             zos.write(buf);
1312             zos.closeArchiveEntry();
1313             zos.close();
1314 
1315             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
1316                 getLengthAndPositionAtCentralDirectory(a);
1317 
1318                 final long cfhPos = a.getFilePointer();
1319                 // grab first entry, verify sizes are not
1320                 // 0xFFFFFFF and it has no ZIP64 extended
1321                 // information extra field
1322                 byte[] header = new byte[12];
1323                 a.readFully(header);
1324                 assertArrayEquals(new byte[] {
1325                         // sig
1326                         (byte) 0x50, (byte) 0x4b, 1, 2,
1327                         // version made by
1328                         20, 0,
1329                         // version needed to extract
1330                         20, 0,
1331                         // GPB (EFS + Data Descriptor)
1332                         8, 8,
1333                         // method
1334                         8, 0, }, header);
1335                 // ignore timestamp
1336                 a.skipBytes(4);
1337                 final byte[] crc = new byte[4];
1338                 a.readFully(crc);
1339                 assertArrayEquals(new byte[] { (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, crc);
1340                 // skip compressed size
1341                 a.skipBytes(4);
1342                 byte[] rest = new byte[23];
1343                 a.readFully(rest);
1344                 assertArrayEquals(new byte[] {
1345                         // Original Size
1346                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1347                         // file name length
1348                         1, 0,
1349                         // extra field length
1350                         0, 0,
1351                         // comment length
1352                         0, 0,
1353                         // disk number
1354                         0, 0,
1355                         // attributes
1356                         0, 0, 0, 0, 0, 0,
1357                         // offset
1358                         0, 0, 0, 0,
1359                         // file name
1360                         (byte) '0' }, rest);
1361 
1362                 // validate data descriptor
1363                 a.seek(cfhPos - 16);
1364                 byte[] dd = new byte[8];
1365                 a.readFully(dd);
1366                 assertArrayEquals(new byte[] {
1367                         // sig
1368                         (byte) 0x50, (byte) 0x4b, 7, 8,
1369                         // CRC
1370                         (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, dd);
1371                 // skip uncompressed size
1372                 a.skipBytes(4);
1373                 dd = new byte[4];
1374                 a.readFully(dd);
1375                 assertArrayEquals(new byte[] {
1376                         // original size
1377                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, }, dd);
1378 
1379                 // and now validate local file header
1380                 a.seek(0);
1381                 header = new byte[10];
1382                 a.readFully(header);
1383                 assertArrayEquals(new byte[] {
1384                         // sig
1385                         (byte) 0x50, (byte) 0x4b, 3, 4,
1386                         // version needed to extract
1387                         20, 0,
1388                         // GPB (EFS + Data Descriptor)
1389                         8, 8,
1390                         // method
1391                         8, 0, }, header);
1392                 // ignore timestamp
1393                 a.skipBytes(4);
1394                 rest = new byte[17];
1395                 a.readFully(rest);
1396                 assertArrayEquals(new byte[] {
1397                         // CRC
1398                         0, 0, 0, 0,
1399                         // Compressed Size
1400                         0, 0, 0, 0,
1401                         // Original Size
1402                         0, 0, 0, 0,
1403                         // file name length
1404                         1, 0,
1405                         // extra field length
1406                         0, 0,
1407                         // file name
1408                         (byte) '0' }, rest);
1409             }
1410         };
1411 
1412     }
1413 
1414     /*
1415      * One entry of length 1 million bytes, written with compression to a stream.
1416      *
1417      * Compression + Stream => sizes are set to 0 in LFH, real values are inside the data descriptor. ZIP64 extra field as mode is Always.
1418      */
1419     private static ZipOutputTest writeSmallDeflatedEntryToStreamModeAlways(final boolean knownSize) {
1420         return (f, zos) -> {
1421             zos.setUseZip64(Zip64Mode.Always);
1422             final byte[] buf = new byte[ONE_MILLION];
1423             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1424             if (knownSize) {
1425                 zae.setSize(ONE_MILLION);
1426             }
1427             zae.setMethod(ZipEntry.DEFLATED);
1428             zos.putArchiveEntry(zae);
1429             zos.write(buf);
1430             zos.closeArchiveEntry();
1431             zos.close();
1432 
1433             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
1434                 getLengthAndPositionAtCentralDirectory(a);
1435 
1436                 final long cfhPos = a.getFilePointer();
1437                 // grab first entry, verify sizes are not
1438                 // 0xFFFFFFF and it has an empty ZIP64 extended
1439                 // information extra field
1440                 byte[] header = new byte[12];
1441                 a.readFully(header);
1442                 assertArrayEquals(new byte[] {
1443                         // sig
1444                         (byte) 0x50, (byte) 0x4b, 1, 2,
1445                         // version made by
1446                         45, 0,
1447                         // version needed to extract
1448                         45, 0,
1449                         // GPB (EFS + Data Descriptor)
1450                         8, 8,
1451                         // method
1452                         8, 0, }, header, "CDH start");
1453                 // ignore timestamp
1454                 a.skipBytes(4);
1455                 final byte[] crc = new byte[4];
1456                 a.readFully(crc);
1457                 assertArrayEquals(new byte[] { (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, crc);
1458                 // skip compressed size
1459                 a.skipBytes(4);
1460                 byte[] rest = new byte[23];
1461                 a.readFully(rest);
1462                 assertArrayEquals(new byte[] {
1463                         // Original Size
1464                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1465                         // file name length
1466                         1, 0,
1467                         // extra field length
1468                         32, 0,
1469                         // comment length
1470                         0, 0,
1471                         // disk number
1472                         0, 0,
1473                         // attributes
1474                         0, 0, 0, 0, 0, 0,
1475                         // offset
1476                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1477                         // file name
1478                         (byte) '0' }, rest, "CDH rest");
1479                 byte[] extra = new byte[12];
1480                 a.readFully(extra);
1481                 assertArrayEquals(new byte[] {
1482                         // Header-ID
1483                         1, 0,
1484                         // size of extra
1485                         28, 0,
1486                         // original size
1487                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0, }, extra, "CDH extra");
1488                 // skip compressed size
1489                 a.skipBytes(8);
1490                 final byte[] offset = new byte[8];
1491                 a.readFully(offset);
1492                 assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, }, offset, "extra offset");
1493 
1494                 // validate data descriptor
1495                 a.seek(cfhPos - 24);
1496                 byte[] dd = new byte[8];
1497                 a.readFully(dd);
1498                 assertArrayEquals(new byte[] {
1499                         // sig
1500                         (byte) 0x50, (byte) 0x4b, 7, 8,
1501                         // CRC
1502                         (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12, }, dd, "DD");
1503                 // skip compressed size
1504                 a.skipBytes(8);
1505                 dd = new byte[8];
1506                 a.readFully(dd);
1507                 assertArrayEquals(new byte[] {
1508                         // original size
1509                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0 }, dd, "DD size");
1510 
1511                 // and now validate local file header
1512                 a.seek(0);
1513                 header = new byte[10];
1514                 a.readFully(header);
1515                 assertArrayEquals(new byte[] {
1516                         // sig
1517                         (byte) 0x50, (byte) 0x4b, 3, 4,
1518                         // version needed to extract
1519                         45, 0,
1520                         // GPB (EFS + Data Descriptor)
1521                         8, 8,
1522                         // method
1523                         8, 0, }, header, "LFH start");
1524                 // ignore timestamp
1525                 a.skipBytes(4);
1526                 rest = new byte[17];
1527                 a.readFully(rest);
1528                 assertArrayEquals(new byte[] {
1529                         // CRC
1530                         0, 0, 0, 0,
1531                         // Compressed Size
1532                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1533                         // Original Size
1534                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1535                         // file name length
1536                         1, 0,
1537                         // extra field length
1538                         20, 0,
1539                         // file name
1540                         (byte) '0' }, rest, "LFH rest");
1541 
1542                 extra = new byte[20];
1543                 a.readFully(extra);
1544                 assertArrayEquals(new byte[] {
1545                         // Header-ID
1546                         1, 0,
1547                         // size of extra
1548                         16, 0,
1549                         // original size
1550                         0, 0, 0, 0, 0, 0, 0, 0,
1551                         // compressed size
1552                         0, 0, 0, 0, 0, 0, 0, 0, }, extra, "LFH extra");
1553             }
1554         };
1555 
1556     }
1557 
1558     private static ZipOutputTest writeSmallStoredEntry(final boolean knownSize) {
1559         return writeSmallStoredEntry(knownSize, Zip64Mode.AsNeeded);
1560     }
1561 
1562     /*
1563      * One entry of length 1 million bytes, written without compression.
1564      *
1565      * No Compression => sizes are stored directly inside the LFH. No Data Descriptor at all. Shouldn't contain any ZIP64 extra field if size was known.
1566      *
1567      * Creates a temporary archive of approx 1MB in size
1568      */
1569     private static ZipOutputTest writeSmallStoredEntry(final boolean knownSize, final Zip64Mode mode) {
1570         return (f, zos) -> {
1571             if (mode != Zip64Mode.AsNeeded) {
1572                 zos.setUseZip64(mode);
1573             }
1574             final byte[] buf = new byte[ONE_MILLION];
1575             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1576             if (knownSize) {
1577                 zae.setSize(ONE_MILLION);
1578                 zae.setCrc(0x1279CB9EL);
1579             }
1580             zae.setMethod(ZipEntry.STORED);
1581             zos.putArchiveEntry(zae);
1582             zos.write(buf);
1583             zos.closeArchiveEntry();
1584             zos.close();
1585 
1586             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
1587                 getLengthAndPositionAtCentralDirectory(a);
1588 
1589                 // grab first CF entry, verify sizes are 1e6 and it
1590                 // has no ZIP64 extended information extra field
1591                 // at all
1592                 byte[] header = new byte[12];
1593                 a.readFully(header);
1594                 assertArrayEquals(new byte[] {
1595                         // sig
1596                         (byte) 0x50, (byte) 0x4b, 1, 2,
1597                         // version made by
1598                         20, 0,
1599                         // version needed to extract
1600                         10, 0,
1601                         // GPB (EFS bit)
1602                         0, 8,
1603                         // method
1604                         0, 0 }, header, "CDH start");
1605                 // ignore timestamp
1606                 a.skipBytes(4);
1607                 byte[] rest = new byte[31];
1608                 a.readFully(rest);
1609                 // 1e6 == 0xF4240
1610                 assertArrayEquals(new byte[] {
1611                         // CRC
1612                         (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12,
1613                         // Compressed Size
1614                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1615                         // Original Size
1616                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1617                         // file name length
1618                         1, 0,
1619                         // extra field length
1620                         0, 0,
1621                         // comment length
1622                         0, 0,
1623                         // disk number
1624                         0, 0,
1625                         // attributes
1626                         0, 0, 0, 0, 0, 0,
1627                         // offset
1628                         0, 0, 0, 0,
1629                         // file name
1630                         (byte) '0' }, rest, "CDH rest");
1631 
1632                 // and now validate local file header: this one
1633                 // has a ZIP64 extra field if and only if size was
1634                 // unknown and mode was not Never or the mode was
1635                 // Always (regardless of size)
1636                 final boolean hasExtra = mode == Zip64Mode.Always || mode == Zip64Mode.AsNeeded && !knownSize;
1637                 a.seek(0);
1638                 header = new byte[10];
1639                 a.readFully(header);
1640                 assertArrayEquals(new byte[] {
1641                         // sig
1642                         (byte) 0x50, (byte) 0x4b, 3, 4,
1643                         // version needed to extract
1644                         10, 0,
1645                         // GPB (EFS bit)
1646                         0, 8,
1647                         // method
1648                         0, 0 }, header, "LFH start");
1649                 // ignore timestamp
1650                 a.skipBytes(4);
1651                 rest = new byte[17];
1652                 a.readFully(rest);
1653                 // 1e6 == 0xF4240
1654                 assertArrayEquals(new byte[] {
1655                         // CRC
1656                         (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12,
1657                         // Compressed Size
1658                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1659                         // Original Size
1660                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
1661                         // file name length
1662                         1, 0,
1663                         // extra field length
1664                         (byte) (!hasExtra ? 0 : 20), 0,
1665                         // file name
1666                         (byte) '0' }, rest, "LFH rest");
1667                 if (hasExtra) {
1668                     final byte[] extra = new byte[20];
1669                     a.readFully(extra);
1670                     assertArrayEquals(new byte[] {
1671                             // Header-ID
1672                             1, 0,
1673                             // size of extra
1674                             16, 0,
1675                             // original size
1676                             (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0,
1677                             // compressed size
1678                             (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0, }, extra, "ZIP64 extra field");
1679                 }
1680             }
1681         };
1682     }
1683 
1684     /*
1685      * One entry of length 1 million bytes, written without compression.
1686      *
1687      * No Compression => sizes are stored directly inside the LFH. No Data Descriptor at all. Contains ZIP64 extra fields because mode is Always
1688      *
1689      * Creates a temporary archive of approx 1MB in size
1690      */
1691     private static ZipOutputTest writeSmallStoredEntryModeAlways(final boolean knownSize) {
1692         return (f, zos) -> {
1693             zos.setUseZip64(Zip64Mode.Always);
1694             final byte[] buf = new byte[ONE_MILLION];
1695             final ZipArchiveEntry zae = new ZipArchiveEntry("0");
1696             if (knownSize) {
1697                 zae.setSize(ONE_MILLION);
1698                 zae.setCrc(0x1279CB9EL);
1699             }
1700             zae.setMethod(ZipEntry.STORED);
1701             zos.putArchiveEntry(zae);
1702             zos.write(buf);
1703             zos.closeArchiveEntry();
1704             zos.close();
1705 
1706             try (RandomAccessFile a = RandomAccessFileMode.READ_ONLY.create(f)) {
1707                 getLengthAndPositionAtCentralDirectory(a);
1708 
1709                 // grab first CF entry, verify sizes are 1e6, and it
1710                 // has an empty ZIP64 extended information extra field
1711                 byte[] header = new byte[12];
1712                 a.readFully(header);
1713                 assertArrayEquals(new byte[] {
1714                         // sig
1715                         (byte) 0x50, (byte) 0x4b, 1, 2,
1716                         // version made by
1717                         45, 0,
1718                         // version needed to extract
1719                         45, 0,
1720                         // GPB (EFS bit)
1721                         0, 8,
1722                         // method
1723                         0, 0 }, header, "CDH start");
1724                 // ignore timestamp
1725                 a.skipBytes(4);
1726                 byte[] rest = new byte[31];
1727                 a.readFully(rest);
1728                 // 1e6 == 0xF4240
1729                 assertArrayEquals(new byte[] {
1730                         // CRC
1731                         (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12,
1732                         // Compressed Size
1733                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1734                         // Original Size
1735                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1736                         // file name length
1737                         1, 0,
1738                         // extra field length
1739                         32, 0,
1740                         // comment length
1741                         0, 0,
1742                         // disk number
1743                         0, 0,
1744                         // attributes
1745                         0, 0, 0, 0, 0, 0,
1746                         // offset
1747                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1748                         // file name
1749                         (byte) '0' }, rest, "CDH rest");
1750 
1751                 byte[] extra = new byte[28];
1752                 a.readFully(extra);
1753                 assertArrayEquals(new byte[] {
1754                         // Header-ID
1755                         1, 0,
1756                         // size of extra
1757                         28, 0,
1758                         // original size
1759                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0,
1760                         // compressed size
1761                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, extra, "CDH extra");
1762 
1763                 // and now validate local file header: this one
1764                 // has a ZIP64 extra field as the mode was
1765                 // Always
1766                 a.seek(0);
1767                 header = new byte[10];
1768                 a.readFully(header);
1769                 assertArrayEquals(new byte[] {
1770                         // sig
1771                         (byte) 0x50, (byte) 0x4b, 3, 4,
1772                         // version needed to extract
1773                         45, 0,
1774                         // GPB (EFS bit)
1775                         0, 8,
1776                         // method
1777                         0, 0 }, header, "LFH start");
1778                 // ignore timestamp
1779                 a.skipBytes(4);
1780                 rest = new byte[17];
1781                 a.readFully(rest);
1782                 // 1e6 == 0xF4240
1783                 assertArrayEquals(new byte[] {
1784                         // CRC
1785                         (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12,
1786                         // Compressed Size
1787                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1788                         // Original Size
1789                         (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
1790                         // file name length
1791                         1, 0,
1792                         // extra field length
1793                         20, 0,
1794                         // file name
1795                         (byte) '0' }, rest, "LFH rest");
1796 
1797                 extra = new byte[20];
1798                 a.readFully(extra);
1799                 assertArrayEquals(new byte[] {
1800                         // Header-ID
1801                         1, 0,
1802                         // size of extra
1803                         16, 0,
1804                         // original size
1805                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0,
1806                         // compressed size
1807                         (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0, 0, 0, 0, 0, }, extra, "LFH extra");
1808             }
1809         };
1810     }
1811 
1812     private File buildZipWithZip64Mode(final String fileName, final Zip64Mode zip64Mode, final File inputFile) throws Throwable {
1813         final File outputFile = getTempFile(fileName);
1814         outputFile.createNewFile();
1815         try (ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile)))) {
1816             zipArchiveOutputStream.setUseZip64(zip64Mode);
1817             zipArchiveOutputStream.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS);
1818 
1819             zipArchiveOutputStream.putArchiveEntry(new ZipArchiveEntry("input.bin"));
1820 
1821             Files.copy(inputFile.toPath(), zipArchiveOutputStream);
1822 
1823             zipArchiveOutputStream.closeArchiveEntry();
1824         }
1825 
1826         return outputFile;
1827     }
1828 
1829     @Test
1830     public void testRead100KFilesGeneratedBy7ZIPUsingInputStream() throws Throwable {
1831         read100KFilesImpl(get100KFileFileGeneratedBy7ZIP());
1832     }
1833 
1834     @Test
1835     public void testRead100KFilesGeneratedBy7ZIPUsingZipFile() throws Throwable {
1836         read100KFilesUsingZipFileImpl(get100KFileFileGeneratedBy7ZIP());
1837     }
1838 
1839     @Test
1840     public void testRead100KFilesGeneratedByJava7JarUsingInputStream() throws Throwable {
1841         read100KFilesImpl(get100KFileFileGeneratedByJava7Jar());
1842     }
1843 
1844     @Test
1845     public void testRead100KFilesGeneratedByJava7JarUsingZipFile() throws Throwable {
1846         read100KFilesUsingZipFileImpl(get100KFileFileGeneratedByJava7Jar());
1847     }
1848 
1849     @Test
1850     public void testRead100KFilesGeneratedByPKZipUsingInputStream() throws Throwable {
1851         read100KFilesImpl(get100KFileFileGeneratedByPKZip());
1852     }
1853 
1854     @Test
1855     public void testRead100KFilesGeneratedByPKZipUsingZipFile() throws Throwable {
1856         read100KFilesUsingZipFileImpl(get100KFileFileGeneratedByPKZip());
1857     }
1858 
1859     @Test
1860     public void testRead100KFilesGeneratedByWinCFUsingInputStream() throws Throwable {
1861         read100KFilesImpl(get100KFileFileGeneratedByWinCF());
1862     }
1863 
1864     @Test
1865     public void testRead100KFilesGeneratedByWinCFUsingZipFile() throws Throwable {
1866         read100KFilesUsingZipFileImpl(get100KFileFileGeneratedByWinCF());
1867     }
1868 
1869     @Test
1870     public void testRead100KFilesGeneratedByWinZIPUsingInputStream() throws Throwable {
1871         read100KFilesImpl(get100KFileFileGeneratedByWinZIP());
1872     }
1873 
1874     @Test
1875     public void testRead100KFilesGeneratedByWinZIPUsingZipFile() throws Throwable {
1876         read100KFilesUsingZipFileImpl(get100KFileFileGeneratedByWinZIP());
1877     }
1878 
1879     @Test
1880     public void testRead100KFilesUsingInputStream() throws Throwable {
1881         read100KFilesImpl(get100KFileFile());
1882     }
1883 
1884     @Test
1885     public void testRead100KFilesUsingZipFile() throws Throwable {
1886         read100KFilesUsingZipFileImpl(get100KFileFile());
1887     }
1888 
1889     @Test
1890     public void testRead3EntriesCreatingBigArchiveFileUsingZipFile() throws Throwable {
1891         withTemporaryArchive("read3EntriesCreatingBigArchiveFileUsingZipFile", (f, zos) -> {
1892             write3EntriesCreatingBigArchiveToStream(zos);
1893             ZipFile zf = null;
1894             try {
1895                 zf = ZipFile.builder().setFile(f).get();
1896                 int idx = 0;
1897                 for (final Enumeration<ZipArchiveEntry> e = zf.getEntriesInPhysicalOrder(); e.hasMoreElements();) {
1898                     final ZipArchiveEntry zae = e.nextElement();
1899                     assertEquals(String.valueOf(idx), zae.getName());
1900                     if (idx++ < 2) {
1901                         assertEquals(FIVE_BILLION / 2, zae.getSize());
1902                     } else {
1903                         assertEquals(1, zae.getSize());
1904                         try (InputStream i = zf.getInputStream(zae)) {
1905                             assertNotNull(i);
1906                             assertEquals(42, i.read());
1907                         }
1908                     }
1909                 }
1910             } finally {
1911                 ZipFile.closeQuietly(zf);
1912             }
1913         }, true);
1914     }
1915 
1916     @Test
1917     public void testRead5GBOfZerosGeneratedBy7ZIPUsingInputStream() throws Throwable {
1918         read5GBOfZerosImpl(get5GBZerosFileGeneratedBy7ZIP(), "5GB_of_Zeros");
1919     }
1920 
1921     @Test
1922     public void testRead5GBOfZerosGeneratedBy7ZIPUsingZipFile() throws Throwable {
1923         read5GBOfZerosUsingZipFileImpl(get5GBZerosFileGeneratedBy7ZIP(), "5GB_of_Zeros");
1924     }
1925 
1926     @Test
1927     public void testRead5GBOfZerosGeneratedByJava7JarUsingInputStream() throws Throwable {
1928         read5GBOfZerosImpl(get5GBZerosFileGeneratedByJava7Jar(), "5GB_of_Zeros");
1929     }
1930 
1931     @Test
1932     public void testRead5GBOfZerosGeneratedByJava7JarUsingZipFile() throws Throwable {
1933         read5GBOfZerosUsingZipFileImpl(get5GBZerosFileGeneratedByJava7Jar(), "5GB_of_Zeros");
1934     }
1935 
1936     @Test
1937     public void testRead5GBOfZerosGeneratedByPKZipUsingInputStream() throws Throwable {
1938         read5GBOfZerosImpl(get5GBZerosFileGeneratedByPKZip(), "zip6/5GB_of_Zeros");
1939     }
1940 
1941     @Test
1942     public void testRead5GBOfZerosGeneratedByPKZipUsingZipFile() throws Throwable {
1943         read5GBOfZerosUsingZipFileImpl(get5GBZerosFileGeneratedByPKZip(), "zip6/5GB_of_Zeros");
1944     }
1945 
1946     @Test
1947     public void testRead5GBOfZerosGeneratedByWinZIPUsingInputStream() throws Throwable {
1948         read5GBOfZerosImpl(get5GBZerosFileGeneratedByWinZIP(), "5GB_of_Zeros");
1949     }
1950 
1951     @Test
1952     public void testRead5GBOfZerosGeneratedByWinZIPUsingZipFile() throws Throwable {
1953         read5GBOfZerosUsingZipFileImpl(get5GBZerosFileGeneratedByWinZIP(), "5GB_of_Zeros");
1954     }
1955 
1956     @Test
1957     public void testRead5GBOfZerosUsingInputStream() throws Throwable {
1958         read5GBOfZerosImpl(get5GBZerosFile(), "5GB_of_Zeros");
1959     }
1960 
1961     @Test
1962     public void testRead5GBOfZerosUsingZipFile() throws Throwable {
1963         read5GBOfZerosUsingZipFileImpl(get5GBZerosFile(), "5GB_of_Zeros");
1964     }
1965 
1966     @Test
1967     public void testReadSelfGenerated100KFilesUsingZipFile() throws Throwable {
1968         withTemporaryArchive("readSelfGenerated100KFilesUsingZipFile()", (f, zos) -> {
1969             write100KFilesToStream(zos);
1970             read100KFilesUsingZipFileImpl(f);
1971         }, true);
1972     }
1973 
1974     @Test
1975     public void testWrite100KFilesFile() throws Throwable {
1976         withTemporaryArchive("write100KFilesFile", write100KFiles(), true);
1977     }
1978 
1979     @Test
1980     public void testWrite100KFilesFileModeAlways() throws Throwable {
1981         withTemporaryArchive("write100KFilesFileModeAlways", write100KFiles(Zip64Mode.Always), true);
1982     }
1983 
1984     @Test
1985     public void testWrite100KFilesFileModeNever() throws Throwable {
1986         withTemporaryArchive("write100KFilesFileModeNever", write100KFilesModeNever, true);
1987     }
1988 
1989     @Test
1990     public void testWrite100KFilesStream() throws Throwable {
1991         withTemporaryArchive("write100KFilesStream", write100KFiles(), false);
1992     }
1993 
1994     @Test
1995     public void testWrite100KFilesStreamModeAlways() throws Throwable {
1996         withTemporaryArchive("write100KFilesStreamModeAlways", write100KFiles(Zip64Mode.Always), false);
1997     }
1998 
1999     @Test
2000     public void testWrite100KFilesStreamModeNever() throws Throwable {
2001         withTemporaryArchive("write100KFilesStreamModeNever", write100KFilesModeNever, false);
2002     }
2003 
2004     @Test
2005     public void testWrite3EntriesCreatingBigArchiveFile() throws Throwable {
2006         withTemporaryArchive("write3EntriesCreatingBigArchiveFile", write3EntriesCreatingBigArchive(), true);
2007     }
2008 
2009     @Test
2010     public void testWrite3EntriesCreatingBigArchiveFileModeAlways() throws Throwable {
2011         withTemporaryArchive("write3EntriesCreatingBigArchiveFileModeAlways", write3EntriesCreatingBigArchive(Zip64Mode.Always), true);
2012     }
2013 
2014     @Test
2015     public void testWrite3EntriesCreatingBigArchiveFileModeNever() throws Throwable {
2016         withTemporaryArchive("write3EntriesCreatingBigArchiveFileModeNever", write3EntriesCreatingBigArchiveModeNever, true);
2017     }
2018 
2019     @Test
2020     public void testWrite3EntriesCreatingBigArchiveStream() throws Throwable {
2021         withTemporaryArchive("write3EntriesCreatingBigArchiveStream", write3EntriesCreatingBigArchive(), false);
2022     }
2023 
2024     @Test
2025     public void testWrite3EntriesCreatingBigArchiveStreamModeAlways() throws Throwable {
2026         withTemporaryArchive("write3EntriesCreatingBigArchiveStreamModeAlways", write3EntriesCreatingBigArchive(Zip64Mode.Always), false);
2027     }
2028 
2029     @Test
2030     public void testWrite3EntriesCreatingBigArchiveStreamModeNever() throws Throwable {
2031         withTemporaryArchive("write3EntriesCreatingBigArchiveStreamModeNever", write3EntriesCreatingBigArchiveModeNever, false);
2032     }
2033 
2034     @Test
2035     public void testWrite3EntriesCreatingManySplitArchiveFileModeAlways() throws Throwable {
2036         // about 76,293 ZIP split segments will be created
2037         withTemporaryArchive("write3EntriesCreatingManySplitArchiveFileModeAlways", write3EntriesCreatingBigArchive(Zip64Mode.Always, true), true, 65536L);
2038     }
2039 
2040     @Test
2041     public void testWrite3EntriesCreatingManySplitArchiveFileModeNever() throws Throwable {
2042         withTemporaryArchive("write3EntriesCreatingManySplitArchiveFileModeNever", write3EntriesCreatingBigArchiveModeNever, true, 65536L);
2043     }
2044 
2045     @Test
2046     public void testWriteAndRead5GBOfZerosUsingZipFile() throws Throwable {
2047         File f = null;
2048         try {
2049             f = write5GBZerosFile("writeAndRead5GBOfZerosUsingZipFile");
2050             read5GBOfZerosUsingZipFileImpl(f, "5GB_of_Zeros");
2051         } finally {
2052             if (f != null) {
2053                 AbstractTest.forceDelete(f);
2054             }
2055         }
2056     }
2057 
2058     @Test
2059     public void testWriteBigDeflatedEntryKnownSizeToFile() throws Throwable {
2060         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToFile", writeBigDeflatedEntryToFile(true), true);
2061     }
2062 
2063     @Test
2064     public void testWriteBigDeflatedEntryKnownSizeToFileModeAlways() throws Throwable {
2065         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToFileModeAlways", writeBigDeflatedEntryToFile(true, Zip64Mode.Always), true);
2066     }
2067 
2068     @Test
2069     public void testWriteBigDeflatedEntryKnownSizeToFileModeNever() throws Throwable {
2070         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToFileModeNever", writeBigDeflatedEntryToFileModeNever(true), true);
2071     }
2072 
2073     @Test
2074     public void testWriteBigDeflatedEntryKnownSizeToStream() throws Throwable {
2075         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToStream", writeBigDeflatedEntryToStream(true, Zip64Mode.AsNeeded), false);
2076     }
2077 
2078     @Test
2079     public void testWriteBigDeflatedEntryKnownSizeToStreamModeAlways() throws Throwable {
2080         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToStreamModeAlways", writeBigDeflatedEntryToStream(true, Zip64Mode.Always), false);
2081     }
2082 
2083     @Test
2084     public void testWriteBigDeflatedEntryKnownSizeToStreamModeNever() throws Throwable {
2085         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToStreamModeNever", (f, zos) -> {
2086             zos.setUseZip64(Zip64Mode.Never);
2087             final Zip64RequiredException ex = assertThrows(Zip64RequiredException.class, () -> {
2088                 final ZipArchiveEntry zae = new ZipArchiveEntry("0");
2089                 zae.setSize(FIVE_BILLION);
2090                 zae.setMethod(ZipEntry.DEFLATED);
2091                 zos.putArchiveEntry(zae);
2092             }, "expected a Zip64RequiredException");
2093             assertTrue(ex.getMessage().startsWith("0's size"));
2094         }, false);
2095     }
2096 
2097     @Test
2098     public void testWriteBigDeflatedEntryUnknownSizeToFile() throws Throwable {
2099         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToFile", writeBigDeflatedEntryToFile(false), true);
2100     }
2101 
2102     @Test
2103     public void testWriteBigDeflatedEntryUnknownSizeToFileModeAlways() throws Throwable {
2104         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToFileModeAlways", writeBigDeflatedEntryToFile(false, Zip64Mode.Always), true);
2105     }
2106 
2107     @Test
2108     public void testWriteBigDeflatedEntryUnknownSizeToFileModeNever() throws Throwable {
2109         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToFileModeNever", writeBigDeflatedEntryToFileModeNever(false), true);
2110     }
2111 
2112     @Test
2113     public void testWriteBigDeflatedEntryUnknownSizeToStream() throws Throwable {
2114         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToStream", writeBigDeflatedEntryUnknownSizeToStream(Zip64Mode.AsNeeded), false);
2115     }
2116 
2117     @Test
2118     public void testWriteBigDeflatedEntryUnknownSizeToStreamModeAlways() throws Throwable {
2119         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToStreamModeAlways", writeBigDeflatedEntryToStream(false, Zip64Mode.Always), false);
2120     }
2121 
2122     @Test
2123     public void testWriteBigDeflatedEntryUnknownSizeToStreamModeNever() throws Throwable {
2124         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToStreamModeNever", writeBigDeflatedEntryUnknownSizeToStream(Zip64Mode.Never), false);
2125     }
2126 
2127     @Test
2128     public void testWriteBigStoredEntryKnownSizeToFile() throws Throwable {
2129         withTemporaryArchive("writeBigStoredEntryKnownSizeToFile", writeBigStoredEntry(true), true);
2130     }
2131 
2132     @Test
2133     public void testWriteBigStoredEntryKnownSizeToFileModeAlways() throws Throwable {
2134         withTemporaryArchive("writeBigStoredEntryKnownSizeToFileModeAlways", writeBigStoredEntry(true, Zip64Mode.Always), true);
2135     }
2136 
2137     @Test
2138     public void testWriteBigStoredEntryKnownSizeToFileModeNever() throws Throwable {
2139         withTemporaryArchive("writeBigStoredEntryKnownSizeToFileModeNever", writeBigStoredEntryModeNever(true), true);
2140     }
2141 
2142     /*
2143      * No Compression + Stream => sizes must be known before data is written.
2144      */
2145     @Test
2146     public void testWriteBigStoredEntryToStream() throws Throwable {
2147         withTemporaryArchive("writeBigStoredEntryToStream", writeBigStoredEntry(true), false);
2148     }
2149 
2150     @Test
2151     public void testWriteBigStoredEntryToStreamModeAlways() throws Throwable {
2152         withTemporaryArchive("writeBigStoredEntryToStreamModeAlways", writeBigStoredEntry(true, Zip64Mode.Always), false);
2153     }
2154 
2155     @Test
2156     public void testWriteBigStoredEntryToStreamModeNever() throws Throwable {
2157         withTemporaryArchive("writeBigStoredEntryToStreamModeNever", writeBigStoredEntryModeNever(true), false);
2158     }
2159 
2160     @Test
2161     public void testWriteBigStoredEntryUnknownSizeToFile() throws Throwable {
2162         withTemporaryArchive("writeBigStoredEntryUnknownSizeToFile", writeBigStoredEntry(false), true);
2163     }
2164 
2165     @Test
2166     public void testWriteBigStoredEntryUnknownSizeToFileModeAlways() throws Throwable {
2167         withTemporaryArchive("writeBigStoredEntryUnknownSizeToFileModeAlways", writeBigStoredEntry(false, Zip64Mode.Always), true);
2168     }
2169 
2170     @Test
2171     public void testWriteBigStoredEntryUnknownSizeToFileModeNever() throws Throwable {
2172         withTemporaryArchive("writeBigStoredEntryUnknownSizeToFileModeNever", writeBigStoredEntryModeNever(false), true);
2173     }
2174 
2175     @Test
2176     public void testWriteSmallDeflatedEntryKnownSizeToFile() throws Throwable {
2177         withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToFile", writeSmallDeflatedEntryToFile(true), true);
2178     }
2179 
2180     @Test
2181     public void testWriteSmallDeflatedEntryKnownSizeToFileModeAlways() throws Throwable {
2182         withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToFileModeAlways", writeSmallDeflatedEntryToFileModeAlways(true), true);
2183     }
2184 
2185     @Test
2186     public void testWriteSmallDeflatedEntryKnownSizeToFileModeNever() throws Throwable {
2187         withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToFileModeNever", writeSmallDeflatedEntryToFile(true, Zip64Mode.Never), true);
2188     }
2189 
2190     @Test
2191     public void testWriteSmallDeflatedEntryKnownSizeToStream() throws Throwable {
2192         withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToStream", writeSmallDeflatedEntryToStream(true, Zip64Mode.AsNeeded), false);
2193     }
2194 
2195     @Test
2196     public void testWriteSmallDeflatedEntryKnownSizeToStreamModeAlways() throws Throwable {
2197         withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToStreamModeAlways", writeSmallDeflatedEntryToStreamModeAlways(true), false);
2198     }
2199 
2200     @Test
2201     public void testWriteSmallDeflatedEntryKnownSizeToStreamModeNever() throws Throwable {
2202         withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToStreamModeNever", writeSmallDeflatedEntryToStream(true, Zip64Mode.Never), false);
2203     }
2204 
2205     @Test
2206     public void testWriteSmallDeflatedEntryUnknownSizeToFile() throws Throwable {
2207         withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToFile", writeSmallDeflatedEntryToFile(false), true);
2208     }
2209 
2210     @Test
2211     public void testWriteSmallDeflatedEntryUnknownSizeToFileModeAlways() throws Throwable {
2212         withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToFileModeAlways", writeSmallDeflatedEntryToFileModeAlways(false), true);
2213     }
2214 
2215     @Test
2216     public void testWriteSmallDeflatedEntryUnknownSizeToFileModeNever() throws Throwable {
2217         withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToFileModeNever", writeSmallDeflatedEntryToFile(false, Zip64Mode.Never), true);
2218     }
2219 
2220     @Test
2221     public void testWriteSmallDeflatedEntryUnknownSizeToStream() throws Throwable {
2222         withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToStream", writeSmallDeflatedEntryToStream(false, Zip64Mode.AsNeeded), false);
2223     }
2224 
2225     @Test
2226     public void testWriteSmallDeflatedEntryUnknownSizeToStreamModeAlways() throws Throwable {
2227         withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToStreamModeAlways", writeSmallDeflatedEntryToStreamModeAlways(false), false);
2228     }
2229 
2230     @Test
2231     public void testWriteSmallDeflatedEntryUnknownSizeToStreamModeNever() throws Throwable {
2232         withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToStreamModeNever", writeSmallDeflatedEntryToStream(false, Zip64Mode.Never), false);
2233     }
2234 
2235     @Test
2236     public void testWriteSmallStoredEntryKnownSizeToFile() throws Throwable {
2237         withTemporaryArchive("writeSmallStoredEntryKnownSizeToFile", writeSmallStoredEntry(true), true);
2238     }
2239 
2240     @Test
2241     public void testWriteSmallStoredEntryKnownSizeToFileModeAlways() throws Throwable {
2242         withTemporaryArchive("writeSmallStoredEntryKnownSizeToFileModeAlways", writeSmallStoredEntryModeAlways(true), true);
2243     }
2244 
2245     @Test
2246     public void testWriteSmallStoredEntryKnownSizeToFileModeNever() throws Throwable {
2247         withTemporaryArchive("writeSmallStoredEntryKnownSizeToFileModeNever", writeSmallStoredEntry(true, Zip64Mode.Never), true);
2248     }
2249 
2250     @Test
2251     public void testWriteSmallStoredEntryToStream() throws Throwable {
2252         withTemporaryArchive("writeSmallStoredEntryToStream", writeSmallStoredEntry(true), false);
2253     }
2254 
2255     @Test
2256     public void testWriteSmallStoredEntryToStreamModeAlways() throws Throwable {
2257         withTemporaryArchive("writeSmallStoredEntryToStreamModeAlways", writeSmallStoredEntryModeAlways(true), false);
2258     }
2259 
2260     @Test
2261     public void testWriteSmallStoredEntryToStreamModeNever() throws Throwable {
2262         withTemporaryArchive("writeSmallStoredEntryToStreamModeNever", writeSmallStoredEntry(true, Zip64Mode.Never), false);
2263     }
2264 
2265     @Test
2266     public void testWriteSmallStoredEntryUnknownSizeToFile() throws Throwable {
2267         withTemporaryArchive("writeSmallStoredEntryUnknownSizeToFile", writeSmallStoredEntry(false), true);
2268     }
2269 
2270     @Test
2271     public void testWriteSmallStoredEntryUnknownSizeToFileModeAlways() throws Throwable {
2272         withTemporaryArchive("writeSmallStoredEntryUnknownSizeToFileModeAlways", writeSmallStoredEntryModeAlways(false), true);
2273     }
2274 
2275     @Test
2276     public void testWriteSmallStoredEntryUnknownSizeToFileModeNever() throws Throwable {
2277         withTemporaryArchive("writeSmallStoredEntryUnknownSizeToFileModeNever", writeSmallStoredEntry(false, Zip64Mode.Never), true);
2278     }
2279 
2280     @Test
2281     public void testZip64ModeAlwaysWithCompatibility() throws Throwable {
2282         final File inputFile = getFile("test3.xml");
2283 
2284         // with Zip64Mode.AlwaysWithCompatibility, the relative header offset and disk number
2285         // start will not be set in extra fields
2286         final File zipUsingModeAlwaysWithCompatibility = buildZipWithZip64Mode("testZip64ModeAlwaysWithCompatibility-output-1",
2287                 Zip64Mode.AlwaysWithCompatibility, inputFile);
2288         final ZipFile zipFileWithAlwaysWithCompatibility = ZipFile.builder().setFile(zipUsingModeAlwaysWithCompatibility).get();
2289         ZipArchiveEntry entry = zipFileWithAlwaysWithCompatibility.getEntries().nextElement();
2290         for (final ZipExtraField extraField : entry.getExtraFields()) {
2291             if (!(extraField instanceof Zip64ExtendedInformationExtraField)) {
2292                 continue;
2293             }
2294 
2295             assertNull(((Zip64ExtendedInformationExtraField) extraField).getRelativeHeaderOffset());
2296             assertNull(((Zip64ExtendedInformationExtraField) extraField).getDiskStartNumber());
2297         }
2298 
2299         // with Zip64Mode.Always, the relative header offset and disk number start will be
2300         // set in extra fields
2301         final File zipUsingModeAlways = buildZipWithZip64Mode("testZip64ModeAlwaysWithCompatibility-output-2", Zip64Mode.Always, inputFile);
2302         final ZipFile zipFileWithAlways = ZipFile.builder().setFile(zipUsingModeAlways).get();
2303         entry = zipFileWithAlways.getEntries().nextElement();
2304         for (final ZipExtraField extraField : entry.getExtraFields()) {
2305             if (!(extraField instanceof Zip64ExtendedInformationExtraField)) {
2306                 continue;
2307             }
2308 
2309             assertNotNull(((Zip64ExtendedInformationExtraField) extraField).getRelativeHeaderOffset());
2310             assertNotNull(((Zip64ExtendedInformationExtraField) extraField).getDiskStartNumber());
2311         }
2312     }
2313 }