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

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