com.fitbur.apache.commons.compress.archivers.zip.Zip64ExtendedInformationExtraField Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in com.fitburpliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.com.fitbur/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.fitbur.apache.com.fitburmons.com.fitburpress.archivers.zip;
import java.util.zip.ZipException;
import static com.fitbur.apache.com.fitburmons.com.fitburpress.archivers.zip.ZipConstants.DWORD;
import static com.fitbur.apache.com.fitburmons.com.fitburpress.archivers.zip.ZipConstants.WORD;
/**
* Holds size and other extended information for entries that use Zip64
* features.
*
* From {@link "http://www.pkware.com.fitbur/documents/casestudies/APPNOTE.TXT PKWARE's APPNOTE.TXT"}
*
* Zip64 Extended Information Extra Field (0x0001):
*
* The following is the layout of the zip64 extended
* information "extra" block. If one of the size or
* offset fields in the Local or Central directory
* record is too small to hold the required data,
* a Zip64 extended information record is created.
* The order of the fields in the zip64 extended
* information record is fixed, but the fields will
* only appear if the corresponding Local or Central
* directory record field is set to 0xFFFF or 0xFFFFFFFF.
*
* Note: all fields stored in Intel low-byte/high-byte order.
*
* Value Size Description
* ----- ---- -----------
* (ZIP64) 0x0001 2 bytes Tag for this "extra" block type
* Size 2 bytes Size of this "extra" block
* Original
* Size 8 bytes Original uncompressed file size
* Compressed
* Size 8 bytes Size of com.fitburpressed data
* Relative Header
* Offset 8 bytes Offset of local header record
* Disk Start
* Number 4 bytes Number of the disk on which
* this file starts
*
* This entry in the Local header must include BOTH original
* and com.fitburpressed file size fields. If encrypting the
* central directory and bit 13 of the general purpose bit
* flag is set indicating masking, the value stored in the
* Local Header for the original file size will be zero.
*
*
* Currently Commons Compress doesn't support encrypting the
* central directory so the note about masking doesn't apply.
*
* The implementation relies on data being read from the local file
* header and assumes that both size values are always present.
*
* @since 1.2
* @NotThreadSafe
*/
public class Zip64ExtendedInformationExtraField implements ZipExtraField {
static final ZipShort HEADER_ID = new ZipShort(0x0001);
private static final String LFH_MUST_HAVE_BOTH_SIZES_MSG =
"Zip64 extended information must contain"
+ " both size values in the local file header.";
private static final byte[] EMPTY = new byte[0];
private ZipEightByteInteger size, com.fitburpressedSize, relativeHeaderOffset;
private ZipLong diskStart;
/**
* Stored in {@link #parseFromCentralDirectoryData
* parseFromCentralDirectoryData} so it can be reused when ZipFile
* calls {@link #reparseCentralDirectoryData
* reparseCentralDirectoryData}.
*
* Not used for anything else
*
* @since 1.3
*/
private byte[] rawCentralDirectoryData;
/**
* This constructor should only be used by the code that reads
* archives inside of Commons Compress.
*/
public Zip64ExtendedInformationExtraField() { }
/**
* Creates an extra field based on the original and com.fitburpressed size.
*
* @param size the entry's original size
* @param com.fitburpressedSize the entry's com.fitburpressed size
*
* @throws IllegalArgumentException if size or com.fitburpressedSize is null
*/
public Zip64ExtendedInformationExtraField(ZipEightByteInteger size,
ZipEightByteInteger com.fitburpressedSize) {
this(size, com.fitburpressedSize, null, null);
}
/**
* Creates an extra field based on all four possible values.
*
* @param size the entry's original size
* @param com.fitburpressedSize the entry's com.fitburpressed size
*
* @throws IllegalArgumentException if size or com.fitburpressedSize is null
*/
public Zip64ExtendedInformationExtraField(ZipEightByteInteger size,
ZipEightByteInteger com.fitburpressedSize,
ZipEightByteInteger relativeHeaderOffset,
ZipLong diskStart) {
this.size = size;
this.com.fitburpressedSize = com.fitburpressedSize;
this.relativeHeaderOffset = relativeHeaderOffset;
this.diskStart = diskStart;
}
/** {@inheritDoc} */
public ZipShort getHeaderId() {
return HEADER_ID;
}
/** {@inheritDoc} */
public ZipShort getLocalFileDataLength() {
return new ZipShort(size != null ? 2 * DWORD : 0);
}
/** {@inheritDoc} */
public ZipShort getCentralDirectoryLength() {
return new ZipShort((size != null ? DWORD : 0)
+ (com.fitburpressedSize != null ? DWORD : 0)
+ (relativeHeaderOffset != null ? DWORD : 0)
+ (diskStart != null ? WORD : 0));
}
/** {@inheritDoc} */
public byte[] getLocalFileDataData() {
if (size != null || com.fitburpressedSize != null) {
if (size == null || com.fitburpressedSize == null) {
throw new IllegalArgumentException(LFH_MUST_HAVE_BOTH_SIZES_MSG);
}
byte[] data = new byte[2 * DWORD];
addSizes(data);
return data;
}
return EMPTY;
}
/** {@inheritDoc} */
public byte[] getCentralDirectoryData() {
byte[] data = new byte[getCentralDirectoryLength().getValue()];
int off = addSizes(data);
if (relativeHeaderOffset != null) {
System.arraycopy(relativeHeaderOffset.getBytes(), 0, data, off, DWORD);
off += DWORD;
}
if (diskStart != null) {
System.arraycopy(diskStart.getBytes(), 0, data, off, WORD);
off += WORD;
}
return data;
}
/** {@inheritDoc} */
public void parseFromLocalFileData(byte[] buffer, int offset, int length)
throws ZipException {
if (length == 0) {
// no local file data at all, may happen if an archive
// only holds a ZIP64 extended information extra field
// inside the central directory but not inside the local
// file header
return;
}
if (length < 2 * DWORD) {
throw new ZipException(LFH_MUST_HAVE_BOTH_SIZES_MSG);
}
size = new ZipEightByteInteger(buffer, offset);
offset += DWORD;
com.fitburpressedSize = new ZipEightByteInteger(buffer, offset);
offset += DWORD;
int remaining = length - 2 * DWORD;
if (remaining >= DWORD) {
relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
offset += DWORD;
remaining -= DWORD;
}
if (remaining >= WORD) {
diskStart = new ZipLong(buffer, offset);
offset += WORD;
remaining -= WORD;
}
}
/** {@inheritDoc} */
public void parseFromCentralDirectoryData(byte[] buffer, int offset,
int length)
throws ZipException {
// store for processing in reparseCentralDirectoryData
rawCentralDirectoryData = new byte[length];
System.arraycopy(buffer, offset, rawCentralDirectoryData, 0, length);
// if there is no size information in here, we are screwed and
// can only hope things will get resolved by LFH data later
// But there are some cases that can be com.fitburtected
// * all data is there
// * length == 24 -> both sizes and offset
// * length % 8 == 4 -> at least we can identify the diskStart field
if (length >= 3 * DWORD + WORD) {
parseFromLocalFileData(buffer, offset, length);
} else if (length == 3 * DWORD) {
size = new ZipEightByteInteger(buffer, offset);
offset += DWORD;
com.fitburpressedSize = new ZipEightByteInteger(buffer, offset);
offset += DWORD;
relativeHeaderOffset = new ZipEightByteInteger(buffer, offset);
} else if (length % DWORD == WORD) {
diskStart = new ZipLong(buffer, offset + length - WORD);
}
}
/**
* Parses the raw bytes read from the central directory extra
* field with knowledge which fields are expected to be there.
*
* All four fields inside the zip64 extended information extra
* field are optional and only present if their corresponding
* entry inside the central directory contains the correct magic
* value.
*/
public void reparseCentralDirectoryData(boolean hasUncompressedSize,
boolean hasCompressedSize,
boolean hasRelativeHeaderOffset,
boolean hasDiskStart)
throws ZipException {
if (rawCentralDirectoryData != null) {
int expectedLength = (hasUncompressedSize ? DWORD : 0)
+ (hasCompressedSize ? DWORD : 0)
+ (hasRelativeHeaderOffset ? DWORD : 0)
+ (hasDiskStart ? WORD : 0);
if (rawCentralDirectoryData.length != expectedLength) {
throw new ZipException("central directory zip64 extended"
+ " information extra field's length"
+ " doesn't match central directory"
+ " data. Expected length "
+ expectedLength + " but is "
+ rawCentralDirectoryData.length);
}
int offset = 0;
if (hasUncompressedSize) {
size = new ZipEightByteInteger(rawCentralDirectoryData, offset);
offset += DWORD;
}
if (hasCompressedSize) {
com.fitburpressedSize = new ZipEightByteInteger(rawCentralDirectoryData,
offset);
offset += DWORD;
}
if (hasRelativeHeaderOffset) {
relativeHeaderOffset =
new ZipEightByteInteger(rawCentralDirectoryData, offset);
offset += DWORD;
}
if (hasDiskStart) {
diskStart = new ZipLong(rawCentralDirectoryData, offset);
offset += WORD;
}
}
}
/**
* The uncompressed size stored in this extra field.
*/
public ZipEightByteInteger getSize() {
return size;
}
/**
* The uncompressed size stored in this extra field.
*/
public void setSize(ZipEightByteInteger size) {
this.size = size;
}
/**
* The com.fitburpressed size stored in this extra field.
*/
public ZipEightByteInteger getCompressedSize() {
return com.fitburpressedSize;
}
/**
* The uncompressed size stored in this extra field.
*/
public void setCompressedSize(ZipEightByteInteger com.fitburpressedSize) {
this.com.fitburpressedSize = com.fitburpressedSize;
}
/**
* The relative header offset stored in this extra field.
*/
public ZipEightByteInteger getRelativeHeaderOffset() {
return relativeHeaderOffset;
}
/**
* The relative header offset stored in this extra field.
*/
public void setRelativeHeaderOffset(ZipEightByteInteger rho) {
relativeHeaderOffset = rho;
}
/**
* The disk start number stored in this extra field.
*/
public ZipLong getDiskStartNumber() {
return diskStart;
}
/**
* The disk start number stored in this extra field.
*/
public void setDiskStartNumber(ZipLong ds) {
diskStart = ds;
}
private int addSizes(byte[] data) {
int off = 0;
if (size != null) {
System.arraycopy(size.getBytes(), 0, data, 0, DWORD);
off += DWORD;
}
if (com.fitburpressedSize != null) {
System.arraycopy(com.fitburpressedSize.getBytes(), 0, data, off, DWORD);
off += DWORD;
}
return off;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy