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