You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ZipOutputStream.java 58 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.zip;
  19. import static org.apache.tools.zip.ZipConstants.DATA_DESCRIPTOR_MIN_VERSION;
  20. import static org.apache.tools.zip.ZipConstants.DWORD;
  21. import static org.apache.tools.zip.ZipConstants.INITIAL_VERSION;
  22. import static org.apache.tools.zip.ZipConstants.SHORT;
  23. import static org.apache.tools.zip.ZipConstants.WORD;
  24. import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC;
  25. import static org.apache.tools.zip.ZipConstants.ZIP64_MAGIC_SHORT;
  26. import static org.apache.tools.zip.ZipConstants.ZIP64_MIN_VERSION;
  27. import static org.apache.tools.zip.ZipLong.putLong;
  28. import static org.apache.tools.zip.ZipShort.putShort;
  29. import java.io.ByteArrayOutputStream;
  30. import java.io.File;
  31. import java.io.FilterOutputStream;
  32. import java.io.IOException;
  33. import java.io.OutputStream;
  34. import java.io.RandomAccessFile;
  35. import java.nio.ByteBuffer;
  36. import java.nio.file.Files;
  37. import java.util.Calendar;
  38. import java.util.Date;
  39. import java.util.HashMap;
  40. import java.util.LinkedList;
  41. import java.util.List;
  42. import java.util.Map;
  43. import java.util.zip.CRC32;
  44. import java.util.zip.Deflater;
  45. import java.util.zip.ZipException;
  46. /**
  47. * Reimplementation of {@link java.util.zip.ZipOutputStream
  48. * java.util.zip.ZipOutputStream} that does handle the extended
  49. * functionality of this package, especially internal/external file
  50. * attributes and extra fields with different layouts for local file
  51. * data and central directory entries.
  52. *
  53. * <p>This class will try to use {@link java.io.RandomAccessFile
  54. * RandomAccessFile} when you know that the output is going to go to a
  55. * file.</p>
  56. *
  57. * <p>If RandomAccessFile cannot be used, this implementation will use
  58. * a Data Descriptor to store size and CRC information for {@link
  59. * #DEFLATED DEFLATED} entries, this means, you don't need to
  60. * calculate them yourself. Unfortunately this is not possible for
  61. * the {@link #STORED STORED} method, here setting the CRC and
  62. * uncompressed size information is required before {@link
  63. * #putNextEntry putNextEntry} can be called.</p>
  64. *
  65. * <p>As of Apache Ant 1.9.0 it transparently supports Zip64
  66. * extensions and thus individual entries and archives larger than 4
  67. * GB or with more than 65536 entries in most cases but explicit
  68. * control is provided via {@link #setUseZip64}. If the stream can not
  69. * user RandomAccessFile and you try to write a ZipEntry of
  70. * unknown size then Zip64 extensions will be disabled by default.</p>
  71. */
  72. public class ZipOutputStream extends FilterOutputStream {
  73. private static final int BUFFER_SIZE = 512;
  74. private static final int LFH_SIG_OFFSET = 0;
  75. private static final int LFH_VERSION_NEEDED_OFFSET = 4;
  76. private static final int LFH_GPB_OFFSET = 6;
  77. private static final int LFH_METHOD_OFFSET = 8;
  78. private static final int LFH_TIME_OFFSET = 10;
  79. private static final int LFH_CRC_OFFSET = 14;
  80. private static final int LFH_COMPRESSED_SIZE_OFFSET = 18;
  81. private static final int LFH_ORIGINAL_SIZE_OFFSET = 22;
  82. private static final int LFH_FILENAME_LENGTH_OFFSET = 26;
  83. private static final int LFH_EXTRA_LENGTH_OFFSET = 28;
  84. private static final int LFH_FILENAME_OFFSET = 30;
  85. private static final int CFH_SIG_OFFSET = 0;
  86. private static final int CFH_VERSION_MADE_BY_OFFSET = 4;
  87. private static final int CFH_VERSION_NEEDED_OFFSET = 6;
  88. private static final int CFH_GPB_OFFSET = 8;
  89. private static final int CFH_METHOD_OFFSET = 10;
  90. private static final int CFH_TIME_OFFSET = 12;
  91. private static final int CFH_CRC_OFFSET = 16;
  92. private static final int CFH_COMPRESSED_SIZE_OFFSET = 20;
  93. private static final int CFH_ORIGINAL_SIZE_OFFSET = 24;
  94. private static final int CFH_FILENAME_LENGTH_OFFSET = 28;
  95. private static final int CFH_EXTRA_LENGTH_OFFSET = 30;
  96. private static final int CFH_COMMENT_LENGTH_OFFSET = 32;
  97. private static final int CFH_DISK_NUMBER_OFFSET = 34;
  98. private static final int CFH_INTERNAL_ATTRIBUTES_OFFSET = 36;
  99. private static final int CFH_EXTERNAL_ATTRIBUTES_OFFSET = 38;
  100. private static final int CFH_LFH_OFFSET = 42;
  101. private static final int CFH_FILENAME_OFFSET = 46;
  102. /**
  103. * indicates if this archive is finished.
  104. */
  105. private boolean finished = false;
  106. /*
  107. * Apparently Deflater.setInput gets slowed down a lot on Sun JVMs
  108. * when it gets handed a really big buffer. See
  109. * https://issues.apache.org/bugzilla/show_bug.cgi?id=45396
  110. *
  111. * Using a buffer size of 8 kB proved to be a good compromise
  112. */
  113. private static final int DEFLATER_BLOCK_SIZE = 8192;
  114. /**
  115. * Compression method for deflated entries.
  116. *
  117. * @since 1.1
  118. */
  119. public static final int DEFLATED = java.util.zip.ZipEntry.DEFLATED;
  120. /**
  121. * Default compression level for deflated entries.
  122. *
  123. * @since Ant 1.7
  124. */
  125. public static final int DEFAULT_COMPRESSION = Deflater.DEFAULT_COMPRESSION;
  126. /**
  127. * Compression method for stored entries.
  128. *
  129. * @since 1.1
  130. */
  131. public static final int STORED = java.util.zip.ZipEntry.STORED;
  132. /**
  133. * default encoding for file names and comment.
  134. */
  135. static final String DEFAULT_ENCODING = null;
  136. /**
  137. * General purpose flag, which indicates that filenames are
  138. * written in utf-8.
  139. * @deprecated use {@link GeneralPurposeBit#UFT8_NAMES_FLAG} instead
  140. */
  141. @Deprecated
  142. public static final int EFS_FLAG = GeneralPurposeBit.UFT8_NAMES_FLAG;
  143. private static final byte[] EMPTY = new byte[0];
  144. /**
  145. * Current entry.
  146. *
  147. * @since 1.1
  148. */
  149. private CurrentEntry entry;
  150. /**
  151. * The file comment.
  152. *
  153. * @since 1.1
  154. */
  155. private String comment = "";
  156. /**
  157. * Compression level for next entry.
  158. *
  159. * @since 1.1
  160. */
  161. private int level = DEFAULT_COMPRESSION;
  162. /**
  163. * Has the compression level changed when compared to the last
  164. * entry?
  165. *
  166. * @since 1.5
  167. */
  168. private boolean hasCompressionLevelChanged = false;
  169. /**
  170. * Default compression method for next entry.
  171. *
  172. * @since 1.1
  173. */
  174. private int method = java.util.zip.ZipEntry.DEFLATED;
  175. /**
  176. * List of ZipEntries written so far.
  177. *
  178. * @since 1.1
  179. */
  180. private final List<ZipEntry> entries = new LinkedList<ZipEntry>();
  181. /**
  182. * CRC instance to avoid parsing DEFLATED data twice.
  183. *
  184. * @since 1.1
  185. */
  186. private final CRC32 crc = new CRC32();
  187. /**
  188. * Count the bytes written to out.
  189. *
  190. * @since 1.1
  191. */
  192. private long written = 0;
  193. /**
  194. * Start of central directory.
  195. *
  196. * @since 1.1
  197. */
  198. private long cdOffset = 0;
  199. /**
  200. * Length of central directory.
  201. *
  202. * @since 1.1
  203. */
  204. private long cdLength = 0;
  205. /**
  206. * Helper, a 0 as ZipShort.
  207. *
  208. * @since 1.1
  209. */
  210. private static final byte[] ZERO = {0, 0};
  211. /**
  212. * Helper, a 0 as ZipLong.
  213. *
  214. * @since 1.1
  215. */
  216. private static final byte[] LZERO = {0, 0, 0, 0};
  217. private static final byte[] ONE = ZipLong.getBytes(1L);
  218. /**
  219. * Holds the offsets of the LFH starts for each entry.
  220. *
  221. * @since 1.1
  222. */
  223. private final Map<ZipEntry, Long> offsets = new HashMap<ZipEntry, Long>();
  224. /**
  225. * The encoding to use for filenames and the file comment.
  226. *
  227. * <p>For a list of possible values see <a
  228. * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
  229. * Defaults to the platform's default character encoding.</p>
  230. *
  231. * @since 1.3
  232. */
  233. private String encoding = null;
  234. /**
  235. * The zip encoding to use for filenames and the file comment.
  236. *
  237. * This field is of internal use and will be set in {@link
  238. * #setEncoding(String)}.
  239. */
  240. private ZipEncoding zipEncoding =
  241. ZipEncodingHelper.getZipEncoding(DEFAULT_ENCODING);
  242. // CheckStyle:VisibilityModifier OFF - bc
  243. /**
  244. * This Deflater object is used for output.
  245. *
  246. */
  247. protected final Deflater def = new Deflater(level, true);
  248. /**
  249. * This buffer serves as a Deflater.
  250. *
  251. * <p>This attribute is only protected to provide a level of API
  252. * backwards compatibility. This class used to extend {@link
  253. * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to
  254. * Revision 1.13.</p>
  255. *
  256. * @since 1.14
  257. */
  258. protected byte[] buf = new byte[BUFFER_SIZE];
  259. // CheckStyle:VisibilityModifier ON
  260. /**
  261. * Optional random access output.
  262. *
  263. * @since 1.14
  264. */
  265. private final RandomAccessFile raf;
  266. /**
  267. * whether to use the general purpose bit flag when writing UTF-8
  268. * filenames or not.
  269. */
  270. private boolean useUTF8Flag = true;
  271. /**
  272. * Whether to encode non-encodable file names as UTF-8.
  273. */
  274. private boolean fallbackToUTF8 = false;
  275. /**
  276. * whether to create UnicodePathExtraField-s for each entry.
  277. */
  278. private UnicodeExtraFieldPolicy createUnicodeExtraFields = UnicodeExtraFieldPolicy.NEVER;
  279. /**
  280. * Whether anything inside this archive has used a ZIP64 feature.
  281. */
  282. private boolean hasUsedZip64 = false;
  283. private Zip64Mode zip64Mode = Zip64Mode.AsNeeded;
  284. private final Calendar calendarInstance = Calendar.getInstance();
  285. /**
  286. * Creates a new ZIP OutputStream filtering the underlying stream.
  287. * @param out the outputstream to zip
  288. * @since 1.1
  289. */
  290. public ZipOutputStream(OutputStream out) {
  291. super(out);
  292. this.raf = null;
  293. }
  294. /**
  295. * Creates a new ZIP OutputStream writing to a File. Will use
  296. * random access if possible.
  297. * @param file the file to zip to
  298. * @since 1.14
  299. * @throws IOException on error
  300. */
  301. public ZipOutputStream(File file) throws IOException {
  302. super(null);
  303. RandomAccessFile _raf = null;
  304. try {
  305. _raf = new RandomAccessFile(file, "rw");
  306. _raf.setLength(0);
  307. } catch (IOException e) {
  308. if (_raf != null) {
  309. try {
  310. _raf.close();
  311. } catch (IOException inner) { // NOPMD
  312. // ignore
  313. }
  314. _raf = null;
  315. }
  316. out = Files.newOutputStream(file.toPath());
  317. }
  318. raf = _raf;
  319. }
  320. /**
  321. * This method indicates whether this archive is writing to a
  322. * seekable stream (i.e., to a random access file).
  323. *
  324. * <p>For seekable streams, you don't need to calculate the CRC or
  325. * uncompressed size for {@link #STORED} entries before
  326. * invoking {@link #putNextEntry}.
  327. * @return true if seekable
  328. * @since 1.17
  329. */
  330. public boolean isSeekable() {
  331. return raf != null;
  332. }
  333. /**
  334. * The encoding to use for filenames and the file comment.
  335. *
  336. * <p>For a list of possible values see <a
  337. * href="http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html">http://java.sun.com/j2se/1.5.0/docs/guide/intl/encoding.doc.html</a>.
  338. * Defaults to the platform's default character encoding.</p>
  339. * @param encoding the encoding value
  340. * @since 1.3
  341. */
  342. public void setEncoding(final String encoding) {
  343. this.encoding = encoding;
  344. this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
  345. if (useUTF8Flag && !ZipEncodingHelper.isUTF8(encoding)) {
  346. useUTF8Flag = false;
  347. }
  348. }
  349. /**
  350. * The encoding to use for filenames and the file comment.
  351. *
  352. * @return null if using the platform's default character encoding.
  353. *
  354. * @since 1.3
  355. */
  356. public String getEncoding() {
  357. return encoding;
  358. }
  359. /**
  360. * Whether to set the language encoding flag if the file name
  361. * encoding is UTF-8.
  362. *
  363. * <p>Defaults to true.</p>
  364. */
  365. public void setUseLanguageEncodingFlag(boolean b) {
  366. useUTF8Flag = b && ZipEncodingHelper.isUTF8(encoding);
  367. }
  368. /**
  369. * Whether to create Unicode Extra Fields.
  370. *
  371. * <p>Defaults to NEVER.</p>
  372. */
  373. public void setCreateUnicodeExtraFields(UnicodeExtraFieldPolicy b) {
  374. createUnicodeExtraFields = b;
  375. }
  376. /**
  377. * Whether to fall back to UTF and the language encoding flag if
  378. * the file name cannot be encoded using the specified encoding.
  379. *
  380. * <p>Defaults to false.</p>
  381. */
  382. public void setFallbackToUTF8(boolean b) {
  383. fallbackToUTF8 = b;
  384. }
  385. /**
  386. * Whether Zip64 extensions will be used.
  387. *
  388. * <p>When setting the mode to {@link Zip64Mode#Never Never},
  389. * {@link #putNextEntry}, {@link #closeEntry}, {@link
  390. * #finish} or {@link #close} may throw a {@link
  391. * Zip64RequiredException} if the entry's size or the total size
  392. * of the archive exceeds 4GB or there are more than 65536 entries
  393. * inside the archive. Any archive created in this mode will be
  394. * readable by implementations that don't support Zip64.</p>
  395. *
  396. * <p>When setting the mode to {@link Zip64Mode#Always Always},
  397. * Zip64 extensions will be used for all entries. Any archive
  398. * created in this mode may be unreadable by implementations that
  399. * don't support Zip64 even if all its contents would be.</p>
  400. *
  401. * <p>When setting the mode to {@link Zip64Mode#AsNeeded
  402. * AsNeeded}, Zip64 extensions will transparently be used for
  403. * those entries that require them. This mode can only be used if
  404. * the uncompressed size of the {@link ZipEntry} is known
  405. * when calling {@link #putNextEntry} or the archive is written
  406. * to a seekable output (i.e. you have used the {@link
  407. * #ZipOutputStream(java.io.File) File-arg constructor}) -
  408. * this mode is not valid when the output stream is not seekable
  409. * and the uncompressed size is unknown when {@link
  410. * #putNextEntry} is called.</p>
  411. *
  412. * <p>If no entry inside the resulting archive requires Zip64
  413. * extensions then {@link Zip64Mode#Never Never} will create the
  414. * smallest archive. {@link Zip64Mode#AsNeeded AsNeeded} will
  415. * create a slightly bigger archive if the uncompressed size of
  416. * any entry has initially been unknown and create an archive
  417. * identical to {@link Zip64Mode#Never Never} otherwise. {@link
  418. * Zip64Mode#Always Always} will create an archive that is at
  419. * least 24 bytes per entry bigger than the one {@link
  420. * Zip64Mode#Never Never} would create.</p>
  421. *
  422. * <p>Defaults to {@link Zip64Mode#AsNeeded AsNeeded} unless
  423. * {@link #putNextEntry} is called with an entry of unknown
  424. * size and data is written to a non-seekable stream - in this
  425. * case the default is {@link Zip64Mode#Never Never}.</p>
  426. *
  427. * @since 1.3
  428. */
  429. public void setUseZip64(Zip64Mode mode) {
  430. zip64Mode = mode;
  431. }
  432. /**
  433. * {@inheritDoc}
  434. * @throws Zip64RequiredException if the archive's size exceeds 4
  435. * GByte or there are more than 65535 entries inside the archive
  436. * and {@link #setUseZip64} is {@link Zip64Mode#Never}.
  437. */
  438. public void finish() throws IOException {
  439. if (finished) {
  440. throw new IOException("This archive has already been finished");
  441. }
  442. if (entry != null) {
  443. closeEntry();
  444. }
  445. cdOffset = written;
  446. writeCentralDirectoryInChunks();
  447. cdLength = written - cdOffset;
  448. writeZip64CentralDirectory();
  449. writeCentralDirectoryEnd();
  450. offsets.clear();
  451. entries.clear();
  452. def.end();
  453. finished = true;
  454. }
  455. private void writeCentralDirectoryInChunks() throws IOException {
  456. final int NUM_PER_WRITE = 1000;
  457. final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(70 * NUM_PER_WRITE);
  458. int count = 0;
  459. for (ZipEntry ze : entries) {
  460. byteArrayOutputStream.write(createCentralFileHeader(ze));
  461. if (++count > NUM_PER_WRITE){
  462. writeCounted(byteArrayOutputStream.toByteArray());
  463. byteArrayOutputStream.reset();
  464. count = 0;
  465. }
  466. }
  467. writeCounted(byteArrayOutputStream.toByteArray());
  468. }
  469. /**
  470. * Writes all necessary data for this entry.
  471. *
  472. * @since 1.1
  473. * @throws IOException on error
  474. * @throws Zip64RequiredException if the entry's uncompressed or
  475. * compressed size exceeds 4 GByte and {@link #setUseZip64}
  476. * is {@link Zip64Mode#Never}.
  477. */
  478. public void closeEntry() throws IOException {
  479. preClose();
  480. flushDeflater();
  481. final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry);
  482. long bytesWritten = written - entry.dataStart;
  483. long realCrc = crc.getValue();
  484. crc.reset();
  485. final boolean actuallyNeedsZip64 =
  486. handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
  487. closeEntry(actuallyNeedsZip64);
  488. }
  489. private void closeEntry(boolean actuallyNeedsZip64) throws IOException {
  490. if (raf != null) {
  491. rewriteSizesAndCrc(actuallyNeedsZip64);
  492. }
  493. writeDataDescriptor(entry.entry);
  494. entry = null;
  495. }
  496. private void preClose() throws IOException {
  497. if (finished) {
  498. throw new IOException("Stream has already been finished");
  499. }
  500. if (entry == null) {
  501. throw new IOException("No current entry to close");
  502. }
  503. if (!entry.hasWritten) {
  504. write(EMPTY, 0, 0);
  505. }
  506. }
  507. /**
  508. * Ensures all bytes sent to the deflater are written to the stream.
  509. */
  510. private void flushDeflater() throws IOException {
  511. if (entry.entry.getMethod() == DEFLATED) {
  512. def.finish();
  513. while (!def.finished()) {
  514. deflate();
  515. }
  516. }
  517. }
  518. /**
  519. * Ensures the current entry's size and CRC information is set to
  520. * the values just written, verifies it isn't too big in the
  521. * Zip64Mode.Never case and returns whether the entry would
  522. * require a Zip64 extra field.
  523. */
  524. private boolean handleSizesAndCrc(long bytesWritten, long crc,
  525. Zip64Mode effectiveMode)
  526. throws ZipException {
  527. if (entry.entry.getMethod() == DEFLATED) {
  528. /* It turns out def.getBytesRead() returns wrong values if
  529. * the size exceeds 4 GB on Java < Java7
  530. entry.entry.setSize(def.getBytesRead());
  531. */
  532. entry.entry.setSize(entry.bytesRead);
  533. entry.entry.setCompressedSize(bytesWritten);
  534. entry.entry.setCrc(crc);
  535. def.reset();
  536. } else if (raf == null) {
  537. if (entry.entry.getCrc() != crc) {
  538. throw new ZipException("bad CRC checksum for entry "
  539. + entry.entry.getName() + ": "
  540. + Long.toHexString(entry.entry.getCrc())
  541. + " instead of "
  542. + Long.toHexString(crc));
  543. }
  544. if (entry.entry.getSize() != bytesWritten) {
  545. throw new ZipException("bad size for entry "
  546. + entry.entry.getName() + ": "
  547. + entry.entry.getSize()
  548. + " instead of "
  549. + bytesWritten);
  550. }
  551. } else { /* method is STORED and we used RandomAccessFile */
  552. entry.entry.setSize(bytesWritten);
  553. entry.entry.setCompressedSize(bytesWritten);
  554. entry.entry.setCrc(crc);
  555. }
  556. return checkIfNeedsZip64(effectiveMode);
  557. }
  558. /**
  559. * Ensures the current entry's size and CRC information is set to
  560. * the values just written, verifies it isn't too big in the
  561. * Zip64Mode.Never case and returns whether the entry would
  562. * require a Zip64 extra field.
  563. */
  564. private boolean checkIfNeedsZip64(Zip64Mode effectiveMode)
  565. throws ZipException {
  566. final boolean actuallyNeedsZip64 = isZip64Required(entry.entry,
  567. effectiveMode);
  568. if (actuallyNeedsZip64 && effectiveMode == Zip64Mode.Never) {
  569. throw new Zip64RequiredException(Zip64RequiredException
  570. .getEntryTooBigMessage(entry.entry));
  571. }
  572. return actuallyNeedsZip64;
  573. }
  574. private boolean isZip64Required(ZipEntry entry1, Zip64Mode requestedMode) {
  575. return requestedMode == Zip64Mode.Always || isTooLageForZip32(entry1);
  576. }
  577. private boolean isTooLageForZip32(ZipEntry zipArchiveEntry){
  578. return zipArchiveEntry.getSize() >= ZIP64_MAGIC
  579. || zipArchiveEntry.getCompressedSize() >= ZIP64_MAGIC;
  580. }
  581. /**
  582. * When using random access output, write the local file header
  583. * and potentiall the ZIP64 extra containing the correct CRC and
  584. * compressed/uncompressed sizes.
  585. */
  586. private void rewriteSizesAndCrc(boolean actuallyNeedsZip64)
  587. throws IOException {
  588. long save = raf.getFilePointer();
  589. raf.seek(entry.localDataStart);
  590. writeOut(ZipLong.getBytes(entry.entry.getCrc()));
  591. if (!hasZip64Extra(entry.entry) || !actuallyNeedsZip64) {
  592. writeOut(ZipLong.getBytes(entry.entry.getCompressedSize()));
  593. writeOut(ZipLong.getBytes(entry.entry.getSize()));
  594. } else {
  595. writeOut(ZipLong.ZIP64_MAGIC.getBytes());
  596. writeOut(ZipLong.ZIP64_MAGIC.getBytes());
  597. }
  598. if (hasZip64Extra(entry.entry)) {
  599. // seek to ZIP64 extra, skip header and size information
  600. raf.seek(entry.localDataStart + 3 * WORD + 2 * SHORT
  601. + getName(entry.entry).limit() + 2 * SHORT);
  602. // inside the ZIP64 extra uncompressed size comes
  603. // first, unlike the LFH, CD or data descriptor
  604. writeOut(ZipEightByteInteger.getBytes(entry.entry.getSize()));
  605. writeOut(ZipEightByteInteger.getBytes(entry.entry.getCompressedSize()));
  606. if (!actuallyNeedsZip64) {
  607. // do some cleanup:
  608. // * rewrite version needed to extract
  609. raf.seek(entry.localDataStart - 5 * SHORT);
  610. writeOut(ZipShort.getBytes(INITIAL_VERSION));
  611. // * remove ZIP64 extra so it doesn't get written
  612. // to the central directory
  613. entry.entry.removeExtraField(Zip64ExtendedInformationExtraField
  614. .HEADER_ID);
  615. entry.entry.setExtra();
  616. // * reset hasUsedZip64 if it has been set because
  617. // of this entry
  618. if (entry.causedUseOfZip64) {
  619. hasUsedZip64 = false;
  620. }
  621. }
  622. }
  623. raf.seek(save);
  624. }
  625. /**
  626. * {@inheritDoc}
  627. * @throws Zip64RequiredException if the entry's uncompressed or
  628. * compressed size is known to exceed 4 GByte and {@link #setUseZip64}
  629. * is {@link Zip64Mode#Never}.
  630. */
  631. public void putNextEntry(ZipEntry archiveEntry) throws IOException {
  632. if (finished) {
  633. throw new IOException("Stream has already been finished");
  634. }
  635. if (entry != null) {
  636. closeEntry();
  637. }
  638. entry = new CurrentEntry(archiveEntry);
  639. entries.add(entry.entry);
  640. setDefaults(entry.entry);
  641. final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry);
  642. validateSizeInformation(effectiveMode);
  643. if (shouldAddZip64Extra(entry.entry, effectiveMode)) {
  644. Zip64ExtendedInformationExtraField z64 = getZip64Extra(entry.entry);
  645. // just a placeholder, real data will be in data
  646. // descriptor or inserted later via RandomAccessFile
  647. ZipEightByteInteger size = ZipEightByteInteger.ZERO;
  648. ZipEightByteInteger compressedSize = ZipEightByteInteger.ZERO;
  649. if (entry.entry.getMethod() == STORED
  650. && entry.entry.getSize() != -1) {
  651. // actually, we already know the sizes
  652. size = new ZipEightByteInteger(entry.entry.getSize());
  653. compressedSize = size;
  654. }
  655. z64.setSize(size);
  656. z64.setCompressedSize(compressedSize);
  657. entry.entry.setExtra();
  658. }
  659. if (entry.entry.getMethod() == DEFLATED && hasCompressionLevelChanged) {
  660. def.setLevel(level);
  661. hasCompressionLevelChanged = false;
  662. }
  663. writeLocalFileHeader(entry.entry);
  664. }
  665. /**
  666. * Provides default values for compression method and last
  667. * modification time.
  668. */
  669. private void setDefaults(ZipEntry entry) {
  670. if (entry.getMethod() == -1) { // not specified
  671. entry.setMethod(method);
  672. }
  673. if (entry.getTime() == -1) { // not specified
  674. entry.setTime(System.currentTimeMillis());
  675. }
  676. }
  677. /**
  678. * Throws an exception if the size is unknown for a stored entry
  679. * that is written to a non-seekable output or the entry is too
  680. * big to be written without Zip64 extra but the mode has been set
  681. * to Never.
  682. */
  683. private void validateSizeInformation(Zip64Mode effectiveMode)
  684. throws ZipException {
  685. // Size/CRC not required if RandomAccessFile is used
  686. if (entry.entry.getMethod() == STORED && raf == null) {
  687. if (entry.entry.getSize() == -1) {
  688. throw new ZipException("uncompressed size is required for"
  689. + " STORED method when not writing to a"
  690. + " file");
  691. }
  692. if (entry.entry.getCrc() == -1) {
  693. throw new ZipException("crc checksum is required for STORED"
  694. + " method when not writing to a file");
  695. }
  696. entry.entry.setCompressedSize(entry.entry.getSize());
  697. }
  698. if ((entry.entry.getSize() >= ZIP64_MAGIC
  699. || entry.entry.getCompressedSize() >= ZIP64_MAGIC)
  700. && effectiveMode == Zip64Mode.Never) {
  701. throw new Zip64RequiredException(Zip64RequiredException
  702. .getEntryTooBigMessage(entry.entry));
  703. }
  704. }
  705. /**
  706. * Whether to addd a Zip64 extended information extra field to the
  707. * local file header.
  708. *
  709. * <p>Returns true if</p>
  710. *
  711. * <ul>
  712. * <li>mode is Always</li>
  713. * <li>or we already know it is going to be needed</li>
  714. * <li>or the size is unknown and we can ensure it won't hurt
  715. * other implementations if we add it (i.e. we can erase its
  716. * usage</li>
  717. * </ul>
  718. */
  719. private boolean shouldAddZip64Extra(ZipEntry entry, Zip64Mode mode) {
  720. return mode == Zip64Mode.Always
  721. || entry.getSize() >= ZIP64_MAGIC
  722. || entry.getCompressedSize() >= ZIP64_MAGIC
  723. || (entry.getSize() == -1
  724. && raf != null && mode != Zip64Mode.Never);
  725. }
  726. /**
  727. * Set the file comment.
  728. * @param comment the comment
  729. */
  730. public void setComment(String comment) {
  731. this.comment = comment;
  732. }
  733. /**
  734. * Sets the compression level for subsequent entries.
  735. *
  736. * <p>Default is Deflater.DEFAULT_COMPRESSION.</p>
  737. * @param level the compression level.
  738. * @throws IllegalArgumentException if an invalid compression
  739. * level is specified.
  740. * @since 1.1
  741. */
  742. public void setLevel(int level) {
  743. if (level < Deflater.DEFAULT_COMPRESSION
  744. || level > Deflater.BEST_COMPRESSION) {
  745. throw new IllegalArgumentException("Invalid compression level: "
  746. + level);
  747. }
  748. hasCompressionLevelChanged = (this.level != level);
  749. this.level = level;
  750. }
  751. /**
  752. * Sets the default compression method for subsequent entries.
  753. *
  754. * <p>Default is DEFLATED.</p>
  755. * @param method an <code>int</code> from java.util.zip.ZipEntry
  756. * @since 1.1
  757. */
  758. public void setMethod(int method) {
  759. this.method = method;
  760. }
  761. /**
  762. * Whether this stream is able to write the given entry.
  763. *
  764. * <p>May return false if it is set up to use encryption or a
  765. * compression method that hasn't been implemented yet.</p>
  766. */
  767. public boolean canWriteEntryData(ZipEntry ae) {
  768. return ZipUtil.canHandleEntryData(ae);
  769. }
  770. /**
  771. * Writes bytes to ZIP entry.
  772. * @param b the byte array to write
  773. * @param offset the start position to write from
  774. * @param length the number of bytes to write
  775. * @throws IOException on error
  776. */
  777. @Override
  778. public void write(byte[] b, int offset, int length) throws IOException {
  779. if (entry == null) {
  780. throw new IllegalStateException("No current entry");
  781. }
  782. ZipUtil.checkRequestedFeatures(entry.entry);
  783. entry.hasWritten = true;
  784. if (entry.entry.getMethod() == DEFLATED) {
  785. writeDeflated(b, offset, length);
  786. } else {
  787. writeCounted(b, offset, length);
  788. }
  789. crc.update(b, offset, length);
  790. }
  791. /**
  792. * Write bytes to output or random access file.
  793. * @param data the byte array to write
  794. * @throws IOException on error
  795. */
  796. private void writeCounted(byte[] data) throws IOException {
  797. writeCounted(data, 0, data.length);
  798. }
  799. private void writeCounted(byte[] data, int offset, int length) throws IOException {
  800. writeOut(data, offset, length);
  801. written += length;
  802. }
  803. /**
  804. * write implementation for DEFLATED entries.
  805. */
  806. private void writeDeflated(byte[]b, int offset, int length)
  807. throws IOException {
  808. if (length > 0 && !def.finished()) {
  809. entry.bytesRead += length;
  810. if (length <= DEFLATER_BLOCK_SIZE) {
  811. def.setInput(b, offset, length);
  812. deflateUntilInputIsNeeded();
  813. } else {
  814. final int fullblocks = length / DEFLATER_BLOCK_SIZE;
  815. for (int i = 0; i < fullblocks; i++) {
  816. def.setInput(b, offset + i * DEFLATER_BLOCK_SIZE,
  817. DEFLATER_BLOCK_SIZE);
  818. deflateUntilInputIsNeeded();
  819. }
  820. final int done = fullblocks * DEFLATER_BLOCK_SIZE;
  821. if (done < length) {
  822. def.setInput(b, offset + done, length - done);
  823. deflateUntilInputIsNeeded();
  824. }
  825. }
  826. }
  827. }
  828. /**
  829. * Closes this output stream and releases any system resources
  830. * associated with the stream.
  831. *
  832. * @exception IOException if an I/O error occurs.
  833. * @throws Zip64RequiredException if the archive's size exceeds 4
  834. * GByte or there are more than 65535 entries inside the archive
  835. * and {@link #setUseZip64} is {@link Zip64Mode#Never}.
  836. */
  837. @Override
  838. public void close() throws IOException {
  839. if (!finished) {
  840. finish();
  841. }
  842. destroy();
  843. }
  844. /**
  845. * Flushes this output stream and forces any buffered output bytes
  846. * to be written out to the stream.
  847. *
  848. * @exception IOException if an I/O error occurs.
  849. */
  850. @Override
  851. public void flush() throws IOException {
  852. if (out != null) {
  853. out.flush();
  854. }
  855. }
  856. /*
  857. * Various ZIP constants
  858. */
  859. /**
  860. * local file header signature
  861. *
  862. * @since 1.1
  863. */
  864. protected static final byte[] LFH_SIG = ZipLong.LFH_SIG.getBytes(); //NOSONAR
  865. /**
  866. * data descriptor signature
  867. *
  868. * @since 1.1
  869. */
  870. protected static final byte[] DD_SIG = ZipLong.DD_SIG.getBytes(); //NOSONAR
  871. /**
  872. * central file header signature
  873. *
  874. * @since 1.1
  875. */
  876. protected static final byte[] CFH_SIG = ZipLong.CFH_SIG.getBytes(); //NOSONAR
  877. /**
  878. * end of central dir signature
  879. *
  880. * @since 1.1
  881. */
  882. protected static final byte[] EOCD_SIG = ZipLong.getBytes(0X06054B50L); //NOSONAR
  883. /**
  884. * ZIP64 end of central dir signature
  885. */
  886. static final byte[] ZIP64_EOCD_SIG = ZipLong.getBytes(0X06064B50L); //NOSONAR
  887. /**
  888. * ZIP64 end of central dir locator signature
  889. */
  890. static final byte[] ZIP64_EOCD_LOC_SIG = ZipLong.getBytes(0X07064B50L); //NOSONAR
  891. /**
  892. * Writes next block of compressed data to the output stream.
  893. * @throws IOException on error
  894. *
  895. * @since 1.14
  896. */
  897. protected final void deflate() throws IOException {
  898. int len = def.deflate(buf, 0, buf.length);
  899. if (len > 0) {
  900. writeCounted(buf, 0, len);
  901. }
  902. }
  903. /**
  904. * Writes the local file header entry
  905. * @param ze the entry to write
  906. * @throws IOException on error
  907. *
  908. * @since 1.1
  909. */
  910. protected void writeLocalFileHeader(ZipEntry ze) throws IOException {
  911. boolean encodable = zipEncoding.canEncode(ze.getName());
  912. ByteBuffer name = getName(ze);
  913. if (createUnicodeExtraFields != UnicodeExtraFieldPolicy.NEVER) {
  914. addUnicodeExtraFields(ze, encodable, name);
  915. }
  916. final byte[] localHeader = createLocalFileHeader(ze, name, encodable);
  917. final long localHeaderStart = written;
  918. offsets.put(ze, localHeaderStart);
  919. entry.localDataStart = localHeaderStart + LFH_CRC_OFFSET; // At crc offset
  920. writeCounted(localHeader);
  921. entry.dataStart = written;
  922. }
  923. private byte[] createLocalFileHeader(ZipEntry ze, ByteBuffer name, boolean encodable) {
  924. byte[] extra = ze.getLocalFileDataExtra();
  925. final int nameLen = name.limit() - name.position();
  926. int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
  927. byte[] buf = new byte[len];
  928. System.arraycopy(LFH_SIG, 0, buf, LFH_SIG_OFFSET, WORD);
  929. //store method in local variable to prevent multiple method calls
  930. final int zipMethod = ze.getMethod();
  931. putShort(versionNeededToExtract(zipMethod, hasZip64Extra(ze)),
  932. buf, LFH_VERSION_NEEDED_OFFSET);
  933. GeneralPurposeBit generalPurposeBit =
  934. getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8);
  935. generalPurposeBit.encode(buf, LFH_GPB_OFFSET);
  936. // compression method
  937. putShort(zipMethod, buf, LFH_METHOD_OFFSET);
  938. ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, LFH_TIME_OFFSET);
  939. // CRC
  940. if (zipMethod == DEFLATED || raf != null) {
  941. System.arraycopy(LZERO, 0, buf, LFH_CRC_OFFSET, WORD);
  942. } else {
  943. putLong(ze.getCrc(), buf, LFH_CRC_OFFSET);
  944. }
  945. // compressed length
  946. // uncompressed length
  947. if (hasZip64Extra(entry.entry)){
  948. // point to ZIP64 extended information extra field for
  949. // sizes, may get rewritten once sizes are known if
  950. // stream is seekable
  951. ZipLong.ZIP64_MAGIC.putLong(buf, LFH_COMPRESSED_SIZE_OFFSET);
  952. ZipLong.ZIP64_MAGIC.putLong(buf, LFH_ORIGINAL_SIZE_OFFSET);
  953. } else if (zipMethod == DEFLATED || raf != null) {
  954. System.arraycopy(LZERO, 0, buf, LFH_COMPRESSED_SIZE_OFFSET, WORD);
  955. System.arraycopy(LZERO, 0, buf, LFH_ORIGINAL_SIZE_OFFSET, WORD);
  956. } else { // Stored
  957. putLong(ze.getSize(), buf, LFH_COMPRESSED_SIZE_OFFSET);
  958. putLong(ze.getSize(), buf, LFH_ORIGINAL_SIZE_OFFSET);
  959. }
  960. // file name length
  961. putShort(nameLen, buf, LFH_FILENAME_LENGTH_OFFSET);
  962. // extra field length
  963. putShort(extra.length, buf, LFH_EXTRA_LENGTH_OFFSET);
  964. // file name
  965. System.arraycopy(name.array(), name.arrayOffset(), buf,
  966. LFH_FILENAME_OFFSET, nameLen);
  967. System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, extra.length);
  968. return buf;
  969. }
  970. /**
  971. * Adds UnicodeExtra fields for name and file comment if mode is
  972. * ALWAYS or the data cannot be encoded using the configured
  973. * encoding.
  974. */
  975. private void addUnicodeExtraFields(ZipEntry ze, boolean encodable,
  976. ByteBuffer name)
  977. throws IOException {
  978. if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS
  979. || !encodable) {
  980. ze.addExtraField(new UnicodePathExtraField(ze.getName(),
  981. name.array(),
  982. name.arrayOffset(),
  983. name.limit()
  984. - name.position()));
  985. }
  986. String comm = ze.getComment();
  987. if (comm != null && !"".equals(comm)) {
  988. boolean commentEncodable = zipEncoding.canEncode(comm);
  989. if (createUnicodeExtraFields == UnicodeExtraFieldPolicy.ALWAYS
  990. || !commentEncodable) {
  991. ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
  992. ze.addExtraField(new UnicodeCommentExtraField(comm,
  993. commentB.array(),
  994. commentB.arrayOffset(),
  995. commentB.limit()
  996. - commentB.position())
  997. );
  998. }
  999. }
  1000. }
  1001. /**
  1002. * Writes the data descriptor entry.
  1003. * @param ze the entry to write
  1004. * @throws IOException on error
  1005. *
  1006. * @since 1.1
  1007. */
  1008. protected void writeDataDescriptor(ZipEntry ze) throws IOException {
  1009. if (ze.getMethod() != DEFLATED || raf != null) {
  1010. return;
  1011. }
  1012. writeCounted(DD_SIG);
  1013. writeCounted(ZipLong.getBytes(ze.getCrc()));
  1014. if (!hasZip64Extra(ze)) {
  1015. writeCounted(ZipLong.getBytes(ze.getCompressedSize()));
  1016. writeCounted(ZipLong.getBytes(ze.getSize()));
  1017. } else {
  1018. writeCounted(ZipEightByteInteger.getBytes(ze.getCompressedSize()));
  1019. writeCounted(ZipEightByteInteger.getBytes(ze.getSize()));
  1020. }
  1021. }
  1022. /**
  1023. * Writes the central file header entry.
  1024. * @param ze the entry to write
  1025. * @throws IOException on error
  1026. * @throws Zip64RequiredException if the archive's size exceeds 4
  1027. * GByte and {@link Zip64Mode #setUseZip64} is {@link
  1028. * Zip64Mode#Never}.
  1029. */
  1030. protected void writeCentralFileHeader(ZipEntry ze) throws IOException {
  1031. byte[] centralFileHeader = createCentralFileHeader(ze);
  1032. writeCounted(centralFileHeader);
  1033. }
  1034. private byte[] createCentralFileHeader(ZipEntry ze) throws IOException {
  1035. final long lfhOffset = offsets.get(ze);
  1036. final boolean needsZip64Extra = hasZip64Extra(ze)
  1037. || ze.getCompressedSize() >= ZIP64_MAGIC
  1038. || ze.getSize() >= ZIP64_MAGIC
  1039. || lfhOffset >= ZIP64_MAGIC;
  1040. if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
  1041. // must be the offset that is too big, otherwise an
  1042. // exception would have been throw in putArchiveEntry or
  1043. // closeArchiveEntry
  1044. throw new Zip64RequiredException(Zip64RequiredException
  1045. .ARCHIVE_TOO_BIG_MESSAGE);
  1046. }
  1047. handleZip64Extra(ze, lfhOffset, needsZip64Extra);
  1048. return createCentralFileHeader(ze, getName(ze), lfhOffset, needsZip64Extra);
  1049. }
  1050. /**
  1051. * Writes the central file header entry.
  1052. * @param ze the entry to write
  1053. * @param name The encoded name
  1054. * @param lfhOffset Local file header offset for this file
  1055. * @throws IOException on error
  1056. */
  1057. private byte[] createCentralFileHeader(ZipEntry ze, ByteBuffer name, long lfhOffset,
  1058. boolean needsZip64Extra) throws IOException {
  1059. byte[] extra = ze.getCentralDirectoryExtra();
  1060. // file comment length
  1061. String comm = ze.getComment();
  1062. if (comm == null) {
  1063. comm = "";
  1064. }
  1065. ByteBuffer commentB = getEntryEncoding(ze).encode(comm);
  1066. final int nameLen = name.limit() - name.position();
  1067. final int commentLen = commentB.limit() - commentB.position();
  1068. int len= CFH_FILENAME_OFFSET + nameLen + extra.length + commentLen;
  1069. byte[] buf = new byte[len];
  1070. System.arraycopy(CFH_SIG, 0, buf, CFH_SIG_OFFSET, WORD);
  1071. // version made by
  1072. // CheckStyle:MagicNumber OFF
  1073. putShort((ze.getPlatform() << 8) | (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION : ZIP64_MIN_VERSION),
  1074. buf, CFH_VERSION_MADE_BY_OFFSET);
  1075. final int zipMethod = ze.getMethod();
  1076. final boolean encodable = zipEncoding.canEncode(ze.getName());
  1077. putShort(versionNeededToExtract(zipMethod, needsZip64Extra), buf, CFH_VERSION_NEEDED_OFFSET);
  1078. getGeneralPurposeBits(zipMethod, !encodable && fallbackToUTF8).encode(buf, CFH_GPB_OFFSET);
  1079. // compression method
  1080. putShort(zipMethod, buf, CFH_METHOD_OFFSET);
  1081. // last mod. time and date
  1082. ZipUtil.toDosTime(calendarInstance, ze.getTime(), buf, CFH_TIME_OFFSET);
  1083. // CRC
  1084. // compressed length
  1085. // uncompressed length
  1086. putLong(ze.getCrc(), buf, CFH_CRC_OFFSET);
  1087. if (ze.getCompressedSize() >= ZIP64_MAGIC
  1088. || ze.getSize() >= ZIP64_MAGIC) {
  1089. ZipLong.ZIP64_MAGIC.putLong(buf, CFH_COMPRESSED_SIZE_OFFSET);
  1090. ZipLong.ZIP64_MAGIC.putLong(buf, CFH_ORIGINAL_SIZE_OFFSET);
  1091. } else {
  1092. putLong(ze.getCompressedSize(), buf, CFH_COMPRESSED_SIZE_OFFSET);
  1093. putLong(ze.getSize(), buf, CFH_ORIGINAL_SIZE_OFFSET);
  1094. }
  1095. putShort(nameLen, buf, CFH_FILENAME_LENGTH_OFFSET);
  1096. // extra field length
  1097. putShort(extra.length, buf, CFH_EXTRA_LENGTH_OFFSET);
  1098. putShort(commentLen, buf, CFH_COMMENT_LENGTH_OFFSET);
  1099. // disk number start
  1100. System.arraycopy(ZERO, 0, buf, CFH_DISK_NUMBER_OFFSET, SHORT);
  1101. // internal file attributes
  1102. putShort(ze.getInternalAttributes(), buf, CFH_INTERNAL_ATTRIBUTES_OFFSET);
  1103. // external file attributes
  1104. putLong(ze.getExternalAttributes(), buf, CFH_EXTERNAL_ATTRIBUTES_OFFSET);
  1105. // relative offset of LFH
  1106. putLong(Math.min(lfhOffset, ZIP64_MAGIC), buf, CFH_LFH_OFFSET);
  1107. // file name
  1108. System.arraycopy(name.array(), name.arrayOffset(), buf, CFH_FILENAME_OFFSET, nameLen);
  1109. int extraStart = CFH_FILENAME_OFFSET + nameLen;
  1110. System.arraycopy(extra, 0, buf, extraStart, extra.length);
  1111. int commentStart = extraStart + extra.length;
  1112. // file comment
  1113. System.arraycopy(commentB.array(), commentB.arrayOffset(), buf, commentStart, commentLen);
  1114. return buf;
  1115. }
  1116. /**
  1117. * If the entry needs Zip64 extra information inside the central
  1118. * directory then configure its data.
  1119. */
  1120. private void handleZip64Extra(ZipEntry ze, long lfhOffset,
  1121. boolean needsZip64Extra) {
  1122. if (needsZip64Extra) {
  1123. Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze);
  1124. if (ze.getCompressedSize() >= ZIP64_MAGIC
  1125. || ze.getSize() >= ZIP64_MAGIC) {
  1126. z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize()));
  1127. z64.setSize(new ZipEightByteInteger(ze.getSize()));
  1128. } else {
  1129. // reset value that may have been set for LFH
  1130. z64.setCompressedSize(null);
  1131. z64.setSize(null);
  1132. }
  1133. if (lfhOffset >= ZIP64_MAGIC) {
  1134. z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset));
  1135. }
  1136. ze.setExtra();
  1137. }
  1138. }
  1139. /**
  1140. * Writes the &quot;End of central dir record&quot;.
  1141. * @throws IOException on error
  1142. * @throws Zip64RequiredException if the archive's size exceeds 4
  1143. * GByte or there are more than 65535 entries inside the archive
  1144. * and {@link Zip64Mode #setUseZip64} is {@link Zip64Mode#Never}.
  1145. */
  1146. protected void writeCentralDirectoryEnd() throws IOException {
  1147. writeCounted(EOCD_SIG);
  1148. // disk numbers
  1149. writeCounted(ZERO);
  1150. writeCounted(ZERO);
  1151. // number of entries
  1152. int numberOfEntries = entries.size();
  1153. if (numberOfEntries > ZIP64_MAGIC_SHORT
  1154. && zip64Mode == Zip64Mode.Never) {
  1155. throw new Zip64RequiredException(Zip64RequiredException
  1156. .TOO_MANY_ENTRIES_MESSAGE);
  1157. }
  1158. if (cdOffset > ZIP64_MAGIC && zip64Mode == Zip64Mode.Never) {
  1159. throw new Zip64RequiredException(Zip64RequiredException
  1160. .ARCHIVE_TOO_BIG_MESSAGE);
  1161. }
  1162. byte[] num = ZipShort.getBytes(Math.min(numberOfEntries,
  1163. ZIP64_MAGIC_SHORT));
  1164. writeCounted(num);
  1165. writeCounted(num);
  1166. // length and location of CD
  1167. writeCounted(ZipLong.getBytes(Math.min(cdLength, ZIP64_MAGIC)));
  1168. writeCounted(ZipLong.getBytes(Math.min(cdOffset, ZIP64_MAGIC)));
  1169. // ZIP file comment
  1170. ByteBuffer data = this.zipEncoding.encode(comment);
  1171. int dataLen = data.limit() - data.position();
  1172. writeCounted(ZipShort.getBytes(dataLen));
  1173. writeCounted(data.array(), data.arrayOffset(), dataLen);
  1174. }
  1175. /**
  1176. * Convert a Date object to a DOS date/time field.
  1177. * @param time the <code>Date</code> to convert
  1178. * @return the date as a <code>ZipLong</code>
  1179. * @since 1.1
  1180. * @deprecated use ZipUtil#toDosTime
  1181. */
  1182. @Deprecated
  1183. protected static ZipLong toDosTime(Date time) {
  1184. return ZipUtil.toDosTime(time);
  1185. }
  1186. /**
  1187. * Convert a Date object to a DOS date/time field.
  1188. *
  1189. * <p>Stolen from InfoZip's <code>fileio.c</code></p>
  1190. * @param t number of milliseconds since the epoch
  1191. * @return the date as a byte array
  1192. * @since 1.26
  1193. * @deprecated use ZipUtil#toDosTime
  1194. */
  1195. @Deprecated
  1196. protected static byte[] toDosTime(long t) {
  1197. return ZipUtil.toDosTime(t);
  1198. }
  1199. /**
  1200. * Retrieve the bytes for the given String in the encoding set for
  1201. * this Stream.
  1202. * @param name the string to get bytes from
  1203. * @return the bytes as a byte array
  1204. * @throws ZipException on error
  1205. *
  1206. * @since 1.3
  1207. */
  1208. protected byte[] getBytes(String name) throws ZipException {
  1209. try {
  1210. ByteBuffer b =
  1211. ZipEncodingHelper.getZipEncoding(encoding).encode(name);
  1212. byte[] result = new byte[b.limit()];
  1213. System.arraycopy(b.array(), b.arrayOffset(), result, 0,
  1214. result.length);
  1215. return result;
  1216. } catch (IOException ex) {
  1217. throw new ZipException("Failed to encode name: " + ex.getMessage());
  1218. }
  1219. }
  1220. /**
  1221. * Writes the &quot;ZIP64 End of central dir record&quot; and
  1222. * &quot;ZIP64 End of central dir locator&quot;.
  1223. * @throws IOException on error
  1224. */
  1225. protected void writeZip64CentralDirectory() throws IOException {
  1226. if (zip64Mode == Zip64Mode.Never) {
  1227. return;
  1228. }
  1229. if (!hasUsedZip64
  1230. && (cdOffset >= ZIP64_MAGIC || cdLength >= ZIP64_MAGIC
  1231. || entries.size() >= ZIP64_MAGIC_SHORT)) {
  1232. // actually "will use"
  1233. hasUsedZip64 = true;
  1234. }
  1235. if (!hasUsedZip64) {
  1236. return;
  1237. }
  1238. long offset = written;
  1239. writeOut(ZIP64_EOCD_SIG);
  1240. // size, we don't have any variable length as we don't support
  1241. // the extensible data sector, yet
  1242. writeOut(ZipEightByteInteger
  1243. .getBytes(SHORT /* version made by */
  1244. + SHORT /* version needed to extract */
  1245. + WORD /* disk number */
  1246. + WORD /* disk with central directory */
  1247. + DWORD /* number of entries in CD on this disk */
  1248. + DWORD /* total number of entries */
  1249. + DWORD /* size of CD */
  1250. + DWORD /* offset of CD */
  1251. ));
  1252. // version made by and version needed to extract
  1253. writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION));
  1254. writeOut(ZipShort.getBytes(ZIP64_MIN_VERSION));
  1255. // disk numbers - four bytes this time
  1256. writeOut(LZERO);
  1257. writeOut(LZERO);
  1258. // number of entries
  1259. byte[] num = ZipEightByteInteger.getBytes(entries.size());
  1260. writeOut(num);
  1261. writeOut(num);
  1262. // length and location of CD
  1263. writeOut(ZipEightByteInteger.getBytes(cdLength));
  1264. writeOut(ZipEightByteInteger.getBytes(cdOffset));
  1265. // no "zip64 extensible data sector" for now
  1266. // and now the "ZIP64 end of central directory locator"
  1267. writeOut(ZIP64_EOCD_LOC_SIG);
  1268. // disk number holding the ZIP64 EOCD record
  1269. writeOut(LZERO);
  1270. // relative offset of ZIP64 EOCD record
  1271. writeOut(ZipEightByteInteger.getBytes(offset));
  1272. // total number of disks
  1273. writeOut(ONE);
  1274. }
  1275. /**
  1276. * Write bytes to output or random access file.
  1277. * @param data the byte array to write
  1278. * @throws IOException on error
  1279. *
  1280. * @since 1.14
  1281. */
  1282. protected final void writeOut(byte[] data) throws IOException {
  1283. writeOut(data, 0, data.length);
  1284. }
  1285. /**
  1286. * Write bytes to output or random access file.
  1287. * @param data the byte array to write
  1288. * @param offset the start position to write from
  1289. * @param length the number of bytes to write
  1290. * @throws IOException on error
  1291. *
  1292. * @since 1.14
  1293. */
  1294. protected final void writeOut(byte[] data, int offset, int length)
  1295. throws IOException {
  1296. if (raf != null) {
  1297. raf.write(data, offset, length);
  1298. } else {
  1299. out.write(data, offset, length);
  1300. }
  1301. }
  1302. /**
  1303. * Assumes a negative integer really is a positive integer that
  1304. * has wrapped around and re-creates the original value.
  1305. * @param i the value to treat as unsigned int.
  1306. * @return the unsigned int as a long.
  1307. * @since 1.34
  1308. * @deprecated use ZipUtil#adjustToLong
  1309. */
  1310. @Deprecated
  1311. protected static long adjustToLong(int i) {
  1312. return ZipUtil.adjustToLong(i);
  1313. }
  1314. private void deflateUntilInputIsNeeded() throws IOException {
  1315. while (!def.needsInput()) {
  1316. deflate();
  1317. }
  1318. }
  1319. private GeneralPurposeBit getGeneralPurposeBits(final int zipMethod, final boolean utfFallback) {
  1320. GeneralPurposeBit b = new GeneralPurposeBit();
  1321. b.useUTF8ForNames(useUTF8Flag || utfFallback);
  1322. if (isDeflatedToOutputStream(zipMethod)) {
  1323. b.useDataDescriptor(true);
  1324. }
  1325. return b;
  1326. }
  1327. private int versionNeededToExtract(final int zipMethod, final boolean zip64) {
  1328. if (zip64) {
  1329. return ZIP64_MIN_VERSION;
  1330. }
  1331. // requires version 2 as we are going to store length info
  1332. // in the data descriptor
  1333. return (isDeflatedToOutputStream(zipMethod)) ?
  1334. DATA_DESCRIPTOR_MIN_VERSION :
  1335. INITIAL_VERSION;
  1336. }
  1337. private boolean isDeflatedToOutputStream(int zipMethod) {
  1338. return zipMethod == DEFLATED && raf == null;
  1339. }
  1340. /**
  1341. * Get the existing ZIP64 extended information extra field or
  1342. * create a new one and add it to the entry.
  1343. */
  1344. private Zip64ExtendedInformationExtraField getZip64Extra(ZipEntry ze) {
  1345. if (entry != null) {
  1346. entry.causedUseOfZip64 = !hasUsedZip64;
  1347. }
  1348. hasUsedZip64 = true;
  1349. Zip64ExtendedInformationExtraField z64 =
  1350. (Zip64ExtendedInformationExtraField)
  1351. ze.getExtraField(Zip64ExtendedInformationExtraField
  1352. .HEADER_ID);
  1353. if (z64 == null) {
  1354. /*
  1355. System.err.println("Adding z64 for " + ze.getName()
  1356. + ", method: " + ze.getMethod()
  1357. + " (" + (ze.getMethod() == STORED) + ")"
  1358. + ", raf: " + (raf != null));
  1359. */
  1360. z64 = new Zip64ExtendedInformationExtraField();
  1361. }
  1362. // even if the field is there already, make sure it is the first one
  1363. ze.addAsFirstExtraField(z64);
  1364. return z64;
  1365. }
  1366. /**
  1367. * Is there a ZIP64 extended information extra field for the
  1368. * entry?
  1369. */
  1370. private boolean hasZip64Extra(ZipEntry ze) {
  1371. return ze.getExtraField(Zip64ExtendedInformationExtraField
  1372. .HEADER_ID)
  1373. != null;
  1374. }
  1375. /**
  1376. * If the mode is AsNeeded and the entry is a compressed entry of
  1377. * unknown size that gets written to a non-seekable stream the
  1378. * change the default to Never.
  1379. */
  1380. private Zip64Mode getEffectiveZip64Mode(ZipEntry ze) {
  1381. if (zip64Mode != Zip64Mode.AsNeeded
  1382. || raf != null
  1383. || ze.getMethod() != DEFLATED
  1384. || ze.getSize() != -1) {
  1385. return zip64Mode;
  1386. }
  1387. return Zip64Mode.Never;
  1388. }
  1389. private ZipEncoding getEntryEncoding(ZipEntry ze) {
  1390. boolean encodable = zipEncoding.canEncode(ze.getName());
  1391. return !encodable && fallbackToUTF8
  1392. ? ZipEncodingHelper.UTF8_ZIP_ENCODING : zipEncoding;
  1393. }
  1394. private ByteBuffer getName(ZipEntry ze) throws IOException {
  1395. return getEntryEncoding(ze).encode(ze.getName());
  1396. }
  1397. /**
  1398. * Closes the underlying stream/file without finishing the
  1399. * archive, the result will likely be a corrupt archive.
  1400. *
  1401. * <p>This method only exists to support tests that generate
  1402. * corrupt archives so they can clean up any temporary files.</p>
  1403. */
  1404. void destroy() throws IOException {
  1405. if (raf != null) {
  1406. raf.close();
  1407. }
  1408. if (out != null) {
  1409. out.close();
  1410. }
  1411. }
  1412. /**
  1413. * enum that represents the possible policies for creating Unicode
  1414. * extra fields.
  1415. */
  1416. public static final class UnicodeExtraFieldPolicy {
  1417. /**
  1418. * Always create Unicode extra fields.
  1419. */
  1420. public static final UnicodeExtraFieldPolicy ALWAYS =
  1421. new UnicodeExtraFieldPolicy("always");
  1422. /**
  1423. * Never create Unicode extra fields.
  1424. */
  1425. public static final UnicodeExtraFieldPolicy NEVER =
  1426. new UnicodeExtraFieldPolicy("never");
  1427. /**
  1428. * Create Unicode extra fields for filenames that cannot be
  1429. * encoded using the specified encoding.
  1430. */
  1431. public static final UnicodeExtraFieldPolicy NOT_ENCODEABLE =
  1432. new UnicodeExtraFieldPolicy("not encodeable");
  1433. private final String name;
  1434. private UnicodeExtraFieldPolicy(String n) {
  1435. name = n;
  1436. }
  1437. @Override
  1438. public String toString() {
  1439. return name;
  1440. }
  1441. }
  1442. /**
  1443. * Structure collecting information for the entry that is
  1444. * currently being written.
  1445. */
  1446. private static final class CurrentEntry {
  1447. private CurrentEntry(ZipEntry entry) {
  1448. this.entry = entry;
  1449. }
  1450. /**
  1451. * Current ZIP entry.
  1452. */
  1453. private final ZipEntry entry;
  1454. /**
  1455. * Offset for CRC entry in the local file header data for the
  1456. * current entry starts here.
  1457. */
  1458. private long localDataStart = 0;
  1459. /**
  1460. * Data for local header data
  1461. */
  1462. private long dataStart = 0;
  1463. /**
  1464. * Number of bytes read for the current entry (can't rely on
  1465. * Deflater#getBytesRead) when using DEFLATED.
  1466. */
  1467. private long bytesRead = 0;
  1468. /**
  1469. * Whether current entry was the first one using ZIP64 features.
  1470. */
  1471. private boolean causedUseOfZip64 = false;
  1472. /**
  1473. * Whether write() has been called at all.
  1474. *
  1475. * <p>In order to create a valid archive {@link
  1476. * #closeEntry closeEntry} will write an empty
  1477. * array to get the CRC right if nothing has been written to
  1478. * the stream at all.</p>
  1479. */
  1480. private boolean hasWritten;
  1481. }
  1482. }