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 53 kB

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