blob: 33701c35732c6460c35c062116ef07b27572317b [file] [log] [blame]
Stefan Bodewig60e2dec2011-07-21 12:11:06 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.commons.compress.archivers.zip;
20
21import java.util.zip.ZipException;
22
Stefan Bodewig43e546e2011-07-29 14:41:25 +000023import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
24import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
25
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000026/**
27 * Holds size and other extended information for entries that use Zip64
28 * features.
29 *
Stefan Bodewigb86d8a62013-07-17 14:37:22 +000030 * <p>See {@link
31 * "http://www.pkware.com/documents/casestudies/APPNOTE.TXT PKWARE's
32 * APPNOTE.TXT, section 4.5.3"}.</p>
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000033 *
34 * <p>Currently Commons Compress doesn't support encrypting the
Stefan Bodewig306c3c82012-06-04 04:43:01 +000035 * central directory so the note about masking doesn't apply.</p>
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000036 *
37 * <p>The implementation relies on data being read from the local file
38 * header and assumes that both size values are always present.</p>
39 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +000040 * @since 1.2
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000041 * @NotThreadSafe
42 */
43public class Zip64ExtendedInformationExtraField implements ZipExtraField {
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000044
Stefan Bodewig6c5f04b2011-07-25 20:14:59 +000045 static final ZipShort HEADER_ID = new ZipShort(0x0001);
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000046
Stefan Bodewig43e546e2011-07-29 14:41:25 +000047 private static final String LFH_MUST_HAVE_BOTH_SIZES_MSG =
48 "Zip64 extended information must contain"
49 + " both size values in the local file header.";
Stefan Bodewiga2f978e2013-01-05 19:28:42 +000050 private static final byte[] EMPTY = new byte[0];
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000051
52 private ZipEightByteInteger size, compressedSize, relativeHeaderOffset;
53 private ZipLong diskStart;
54
55 /**
Stefan Bodewig80447392011-08-04 05:25:28 +000056 * Stored in {@link #parseFromCentralDirectoryData
57 * parseFromCentralDirectoryData} so it can be reused when ZipFile
58 * calls {@link #reparseCentralDirectoryData
59 * reparseCentralDirectoryData}.
60 *
61 * <p>Not used for anything else</p>
62 *
Gary D. Gregory2bd0dd42012-04-01 13:02:39 +000063 * @since 1.3
Stefan Bodewig80447392011-08-04 05:25:28 +000064 */
65 private byte[] rawCentralDirectoryData;
66
67 /**
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000068 * This constructor should only be used by the code that reads
69 * archives inside of Commons Compress.
70 */
71 public Zip64ExtendedInformationExtraField() { }
72
73 /**
74 * Creates an extra field based on the original and compressed size.
75 *
76 * @param size the entry's original size
77 * @param compressedSize the entry's compressed size
78 *
79 * @throws IllegalArgumentException if size or compressedSize is null
80 */
81 public Zip64ExtendedInformationExtraField(ZipEightByteInteger size,
82 ZipEightByteInteger compressedSize) {
83 this(size, compressedSize, null, null);
84 }
85
86 /**
87 * Creates an extra field based on all four possible values.
88 *
89 * @param size the entry's original size
90 * @param compressedSize the entry's compressed size
91 *
92 * @throws IllegalArgumentException if size or compressedSize is null
93 */
94 public Zip64ExtendedInformationExtraField(ZipEightByteInteger size,
95 ZipEightByteInteger compressedSize,
96 ZipEightByteInteger relativeHeaderOffset,
97 ZipLong diskStart) {
Stefan Bodewig60e2dec2011-07-21 12:11:06 +000098 this.size = size;
99 this.compressedSize = compressedSize;
100 this.relativeHeaderOffset = relativeHeaderOffset;
101 this.diskStart = diskStart;
102 }
103
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000104 public ZipShort getHeaderId() {
105 return HEADER_ID;
106 }
107
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000108 public ZipShort getLocalFileDataLength() {
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000109 return new ZipShort(size != null ? 2 * DWORD : 0);
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000110 }
111
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000112 public ZipShort getCentralDirectoryLength() {
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000113 return new ZipShort((size != null ? DWORD : 0)
114 + (compressedSize != null ? DWORD : 0)
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000115 + (relativeHeaderOffset != null ? DWORD : 0)
116 + (diskStart != null ? WORD : 0));
117 }
118
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000119 public byte[] getLocalFileDataData() {
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000120 if (size != null || compressedSize != null) {
121 if (size == null || compressedSize == null) {
122 throw new IllegalArgumentException(LFH_MUST_HAVE_BOTH_SIZES_MSG);
123 }
124 byte[] data = new byte[2 * DWORD];
125 addSizes(data);
126 return data;
127 }
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000128 return EMPTY;
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000129 }
130
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000131 public byte[] getCentralDirectoryData() {
132 byte[] data = new byte[getCentralDirectoryLength().getValue()];
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000133 int off = addSizes(data);
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000134 if (relativeHeaderOffset != null) {
135 System.arraycopy(relativeHeaderOffset.getBytes(), 0, data, off, DWORD);
136 off += DWORD;
137 }
138 if (diskStart != null) {
139 System.arraycopy(diskStart.getBytes(), 0, data, off, WORD);
140 off += WORD;
141 }
142 return data;
143 }
144
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000145 public void parseFromLocalFileData(byte[] buffer, int offset, int length)
146 throws ZipException {
Stefan Bodewig46701aa2011-08-04 11:26:48 +0000147 if (length == 0) {
148 // no local file data at all, may happen if an archive
149 // only holds a ZIP64 extended information extra field
150 // inside the central directory but not inside the local
151 // file header
152 return;
153 }
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000154 if (length < 2 * DWORD) {
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000155 throw new ZipException(LFH_MUST_HAVE_BOTH_SIZES_MSG);
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000156 }
157 size = new ZipEightByteInteger(buffer, offset);
158 offset += DWORD;
159 compressedSize = new ZipEightByteInteger(buffer, offset);
160 offset += DWORD;
161 int remaining = length - 2 * DWORD;
162 if (remaining >= DWORD) {
163 relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
164 offset += DWORD;
165 remaining -= DWORD;
166 }
167 if (remaining >= WORD) {
168 diskStart = new ZipLong(buffer, offset);
169 offset += WORD;
170 remaining -= WORD;
171 }
172 }
173
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000174 public void parseFromCentralDirectoryData(byte[] buffer, int offset,
175 int length)
176 throws ZipException {
Stefan Bodewig80447392011-08-04 05:25:28 +0000177 // store for processing in reparseCentralDirectoryData
178 rawCentralDirectoryData = new byte[length];
179 System.arraycopy(buffer, offset, rawCentralDirectoryData, 0, length);
180
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000181 // if there is no size information in here, we are screwed and
182 // can only hope things will get resolved by LFH data later
183 // But there are some cases that can be detected
184 // * all data is there
Stefan Bodewiga0581332011-07-29 15:05:52 +0000185 // * length == 24 -> both sizes and offset
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000186 // * length % 8 == 4 -> at least we can identify the diskStart field
187 if (length >= 3 * DWORD + WORD) {
188 parseFromLocalFileData(buffer, offset, length);
Stefan Bodewiga0581332011-07-29 15:05:52 +0000189 } else if (length == 3 * DWORD) {
190 size = new ZipEightByteInteger(buffer, offset);
191 offset += DWORD;
192 compressedSize = new ZipEightByteInteger(buffer, offset);
193 offset += DWORD;
194 relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000195 } else if (length % DWORD == WORD) {
196 diskStart = new ZipLong(buffer, offset + length - WORD);
197 }
198 }
199
200 /**
Stefan Bodewig80447392011-08-04 05:25:28 +0000201 * Parses the raw bytes read from the central directory extra
202 * field with knowledge which fields are expected to be there.
203 *
204 * <p>All four fields inside the zip64 extended information extra
Stefan Bodewige860d2f2013-05-26 17:36:35 +0000205 * field are optional and must only be present if their corresponding
Stefan Bodewig80447392011-08-04 05:25:28 +0000206 * entry inside the central directory contains the correct magic
207 * value.</p>
208 */
209 public void reparseCentralDirectoryData(boolean hasUncompressedSize,
210 boolean hasCompressedSize,
211 boolean hasRelativeHeaderOffset,
212 boolean hasDiskStart)
213 throws ZipException {
214 if (rawCentralDirectoryData != null) {
215 int expectedLength = (hasUncompressedSize ? DWORD : 0)
216 + (hasCompressedSize ? DWORD : 0)
217 + (hasRelativeHeaderOffset ? DWORD : 0)
218 + (hasDiskStart ? WORD : 0);
Stefan Bodewige860d2f2013-05-26 17:36:35 +0000219 if (rawCentralDirectoryData.length < expectedLength) {
Stefan Bodewig80447392011-08-04 05:25:28 +0000220 throw new ZipException("central directory zip64 extended"
221 + " information extra field's length"
222 + " doesn't match central directory"
223 + " data. Expected length "
224 + expectedLength + " but is "
225 + rawCentralDirectoryData.length);
226 }
227 int offset = 0;
228 if (hasUncompressedSize) {
229 size = new ZipEightByteInteger(rawCentralDirectoryData, offset);
230 offset += DWORD;
231 }
232 if (hasCompressedSize) {
233 compressedSize = new ZipEightByteInteger(rawCentralDirectoryData,
234 offset);
235 offset += DWORD;
236 }
237 if (hasRelativeHeaderOffset) {
238 relativeHeaderOffset =
239 new ZipEightByteInteger(rawCentralDirectoryData, offset);
240 offset += DWORD;
241 }
242 if (hasDiskStart) {
243 diskStart = new ZipLong(rawCentralDirectoryData, offset);
244 offset += WORD;
245 }
246 }
247 }
248
249 /**
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000250 * The uncompressed size stored in this extra field.
251 */
252 public ZipEightByteInteger getSize() {
253 return size;
254 }
255
256 /**
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000257 * The uncompressed size stored in this extra field.
258 */
259 public void setSize(ZipEightByteInteger size) {
260 this.size = size;
261 }
262
263 /**
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000264 * The compressed size stored in this extra field.
265 */
266 public ZipEightByteInteger getCompressedSize() {
267 return compressedSize;
268 }
269
270 /**
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000271 * The uncompressed size stored in this extra field.
272 */
273 public void setCompressedSize(ZipEightByteInteger compressedSize) {
274 this.compressedSize = compressedSize;
275 }
276
277 /**
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000278 * The relative header offset stored in this extra field.
279 */
280 public ZipEightByteInteger getRelativeHeaderOffset() {
281 return relativeHeaderOffset;
282 }
283
284 /**
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000285 * The relative header offset stored in this extra field.
286 */
287 public void setRelativeHeaderOffset(ZipEightByteInteger rho) {
288 relativeHeaderOffset = rho;
289 }
290
291 /**
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000292 * The disk start number stored in this extra field.
293 */
294 public ZipLong getDiskStartNumber() {
295 return diskStart;
296 }
297
Stefan Bodewig43e546e2011-07-29 14:41:25 +0000298 /**
299 * The disk start number stored in this extra field.
300 */
301 public void setDiskStartNumber(ZipLong ds) {
302 diskStart = ds;
303 }
304
305 private int addSizes(byte[] data) {
306 int off = 0;
307 if (size != null) {
308 System.arraycopy(size.getBytes(), 0, data, 0, DWORD);
309 off += DWORD;
310 }
311 if (compressedSize != null) {
312 System.arraycopy(compressedSize.getBytes(), 0, data, off, DWORD);
313 off += DWORD;
314 }
315 return off;
Stefan Bodewig60e2dec2011-07-21 12:11:06 +0000316 }
Stefan Bodewiga2f978e2013-01-05 19:28:42 +0000317}