git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@749610 13f79535-47bb-0310-9956-ffa450edef68master
@@ -0,0 +1,40 @@ | |||||
/* | |||||
* 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 compliance with | |||||
* the License. You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/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 org.apache.tools.zip; | |||||
import java.util.zip.ZipException; | |||||
/** | |||||
* {@link ZipExtraField ZipExtraField} that knows how to parse central | |||||
* directory data. | |||||
* | |||||
* @since Ant 1.8.0 | |||||
*/ | |||||
public interface CentralDirectoryParsingZipExtraField extends ZipExtraField { | |||||
/** | |||||
* Populate data from this array as if it was in central directory data. | |||||
* @param data an array of bytes | |||||
* @param offset the start offset | |||||
* @param length the number of bytes in the array from offset | |||||
* | |||||
* @throws ZipException on error | |||||
*/ | |||||
void parseFromCentralDirectoryData(byte[] data, int offset, int length) | |||||
throws ZipException; | |||||
} |
@@ -92,13 +92,27 @@ public class ExtraFieldUtils { | |||||
/** | /** | ||||
* Split the array into ExtraFields and populate them with the | * Split the array into ExtraFields and populate them with the | ||||
* give data. | |||||
* given data as local file data. | |||||
* @param data an array of bytes as it appears in local file data | |||||
* @return an array of ExtraFields | |||||
* @throws ZipException on error | |||||
*/ | |||||
public static ZipExtraField[] parse(byte[] data) throws ZipException { | |||||
return parse(data, true); | |||||
} | |||||
/** | |||||
* Split the array into ExtraFields and populate them with the | |||||
* given data. | |||||
* @param data an array of bytes | * @param data an array of bytes | ||||
* @param local whether data originates from the local file data | |||||
* or the central directory | |||||
* @return an array of ExtraFields | * @return an array of ExtraFields | ||||
* @since 1.1 | * @since 1.1 | ||||
* @throws ZipException on error | * @throws ZipException on error | ||||
*/ | */ | ||||
public static ZipExtraField[] parse(byte[] data) throws ZipException { | |||||
public static ZipExtraField[] parse(byte[] data, boolean local) | |||||
throws ZipException { | |||||
List v = new ArrayList(); | List v = new ArrayList(); | ||||
int start = 0; | int start = 0; | ||||
while (start <= data.length - WORD) { | while (start <= data.length - WORD) { | ||||
@@ -110,7 +124,14 @@ public class ExtraFieldUtils { | |||||
} | } | ||||
try { | try { | ||||
ZipExtraField ze = createExtraField(headerId); | ZipExtraField ze = createExtraField(headerId); | ||||
ze.parseFromLocalFileData(data, start + WORD, length); | |||||
if (local | |||||
|| !(ze instanceof CentralDirectoryParsingZipExtraField)) { | |||||
ze.parseFromLocalFileData(data, start + WORD, length); | |||||
} else { | |||||
((CentralDirectoryParsingZipExtraField) ze) | |||||
.parseFromCentralDirectoryData(data, start + WORD, | |||||
length); | |||||
} | |||||
v.add(ze); | v.add(ze); | ||||
} catch (InstantiationException ie) { | } catch (InstantiationException ie) { | ||||
throw new ZipException(ie.getMessage()); | throw new ZipException(ie.getMessage()); | ||||
@@ -26,7 +26,8 @@ package org.apache.tools.zip; | |||||
* identical - unless told the opposite.</p> | * identical - unless told the opposite.</p> | ||||
* | * | ||||
*/ | */ | ||||
public class UnrecognizedExtraField implements ZipExtraField { | |||||
public class UnrecognizedExtraField | |||||
implements CentralDirectoryParsingZipExtraField { | |||||
/** | /** | ||||
* The Header-ID. | * The Header-ID. | ||||
@@ -135,6 +136,22 @@ public class UnrecognizedExtraField implements ZipExtraField { | |||||
setLocalFileDataData(tmp); | setLocalFileDataData(tmp); | ||||
} | } | ||||
/** | |||||
* @param data the array of bytes. | |||||
* @param offset the source location in the data array. | |||||
* @param length the number of bytes to use in the data array. | |||||
* @see ZipExtraField#parseFromCentralDirectoryData(byte[], int, int) | |||||
*/ | |||||
public void parseFromCentralDirectoryData(byte[] data, int offset, | |||||
int length) { | |||||
byte[] tmp = new byte[length]; | |||||
System.arraycopy(data, offset, tmp, 0, length); | |||||
setCentralDirectoryData(tmp); | |||||
if (localData == null) { | |||||
setLocalFileDataData(tmp); | |||||
} | |||||
} | |||||
private static byte[] copy(byte[] from) { | private static byte[] copy(byte[] from) { | ||||
if (from != null) { | if (from != null) { | ||||
byte[] to = new byte[from.length]; | byte[] to = new byte[from.length]; | ||||
@@ -284,7 +284,8 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { | |||||
*/ | */ | ||||
public void setExtra(byte[] extra) throws RuntimeException { | public void setExtra(byte[] extra) throws RuntimeException { | ||||
try { | try { | ||||
setExtraFields(ExtraFieldUtils.parse(extra)); | |||||
ZipExtraField[] local = ExtraFieldUtils.parse(extra, true); | |||||
mergeExtraFields(local, true); | |||||
} catch (Exception e) { | } catch (Exception e) { | ||||
throw new RuntimeException(e.getMessage(), e); | throw new RuntimeException(e.getMessage(), e); | ||||
} | } | ||||
@@ -302,6 +303,18 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { | |||||
super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields())); | super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields())); | ||||
} | } | ||||
/** | |||||
* Sets the central directory part of extra fields. | |||||
*/ | |||||
public void setCentralDirectoryExtra(byte[] b) { | |||||
try { | |||||
ZipExtraField[] central = ExtraFieldUtils.parse(b, false); | |||||
mergeExtraFields(central, false); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e.getMessage(), e); | |||||
} | |||||
} | |||||
/** | /** | ||||
* Retrieves the extra data for the local file data. | * Retrieves the extra data for the local file data. | ||||
* @return the extra data for local file | * @return the extra data for local file | ||||
@@ -386,4 +399,37 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { | |||||
return (this == o); | return (this == o); | ||||
} | } | ||||
/** | |||||
* If there are no extra fields, use the given fields as new extra | |||||
* data - otherwise merge the fields assuming the existing fields | |||||
* and the new fields stem from different locations inside the | |||||
* archive. | |||||
* @param f the extra fields to merge | |||||
* @param local whether the new fields originate from local data | |||||
*/ | |||||
private void mergeExtraFields(ZipExtraField[] f, boolean local) | |||||
throws ZipException { | |||||
if (extraFields == null) { | |||||
setExtraFields(f); | |||||
} else { | |||||
for (int i = 0; i < f.length; i++) { | |||||
ZipExtraField existing = getExtraField(f[i].getHeaderId()); | |||||
if (existing == null) { | |||||
addExtraField(f[i]); | |||||
} else { | |||||
if (local | |||||
|| !(existing | |||||
instanceof CentralDirectoryParsingZipExtraField)) { | |||||
byte[] b = f[i].getLocalFileDataData(); | |||||
existing.parseFromLocalFileData(b, 0, b.length); | |||||
} else { | |||||
byte[] b = f[i].getCentralDirectoryData(); | |||||
((CentralDirectoryParsingZipExtraField) existing) | |||||
.parseFromCentralDirectoryData(b, 0, b.length); | |||||
} | |||||
} | |||||
} | |||||
setExtra(); | |||||
} | |||||
} | |||||
} | } |
@@ -389,15 +389,9 @@ public class ZipFile { | |||||
nameMap.put(ze.getName(), ze); | nameMap.put(ze.getName(), ze); | ||||
int lenToSkip = extraLen; | |||||
while (lenToSkip > 0) { | |||||
int skipped = archive.skipBytes(lenToSkip); | |||||
if (skipped <= 0) { | |||||
throw new RuntimeException("failed to skip extra data in" | |||||
+ " central directory"); | |||||
} | |||||
lenToSkip -= skipped; | |||||
} | |||||
byte[] cdExtraData = new byte[extraLen]; | |||||
archive.readFully(cdExtraData); | |||||
ze.setCentralDirectoryExtra(cdExtraData); | |||||
byte[] comment = new byte[commentLen]; | byte[] comment = new byte[commentLen]; | ||||
archive.readFully(comment); | archive.readFully(comment); | ||||
@@ -32,8 +32,6 @@ public class ZipEntryTest extends TestCase { | |||||
/** | /** | ||||
* test handling of extra fields | * test handling of extra fields | ||||
* | |||||
* @since 1.1 | |||||
*/ | */ | ||||
public void testExtraFields() { | public void testExtraFields() { | ||||
AsiExtraField a = new AsiExtraField(); | AsiExtraField a = new AsiExtraField(); | ||||
@@ -85,10 +83,52 @@ public class ZipEntryTest extends TestCase { | |||||
} | } | ||||
} | } | ||||
/** | |||||
* test handling of extra fields via central directory | |||||
*/ | |||||
public void testExtraFieldMerging() { | |||||
AsiExtraField a = new AsiExtraField(); | |||||
a.setDirectory(true); | |||||
a.setMode(0755); | |||||
UnrecognizedExtraField u = new UnrecognizedExtraField(); | |||||
u.setHeaderId(new ZipShort(1)); | |||||
u.setLocalFileDataData(new byte[0]); | |||||
ZipEntry ze = new ZipEntry("test/"); | |||||
ze.setExtraFields(new ZipExtraField[] {a, u}); | |||||
// merge | |||||
// Header-ID 1 + length 1 + one byte of data | |||||
ze.setCentralDirectoryExtra(new byte[] {1, 0, 1, 0, 127}); | |||||
ZipExtraField[] result = ze.getExtraFields(); | |||||
assertEquals("first pass", 2, result.length); | |||||
assertSame(a, result[0]); | |||||
assertEquals(new ZipShort(1), result[1].getHeaderId()); | |||||
assertEquals(new ZipShort(0), result[1].getLocalFileDataLength()); | |||||
assertEquals(new ZipShort(1), result[1].getCentralDirectoryLength()); | |||||
// add new | |||||
// Header-ID 2 + length 0 | |||||
ze.setCentralDirectoryExtra(new byte[] {2, 0, 0, 0}); | |||||
result = ze.getExtraFields(); | |||||
assertEquals("second pass", 3, result.length); | |||||
// merge | |||||
// Header-ID 2 + length 1 + one byte of data | |||||
ze.setExtra(new byte[] {2, 0, 1, 0, 127}); | |||||
result = ze.getExtraFields(); | |||||
assertEquals("third pass", 3, result.length); | |||||
assertSame(a, result[0]); | |||||
assertEquals(new ZipShort(2), result[2].getHeaderId()); | |||||
assertEquals(new ZipShort(1), result[2].getLocalFileDataLength()); | |||||
assertEquals(new ZipShort(0), result[2].getCentralDirectoryLength()); | |||||
} | |||||
/** | /** | ||||
* test handling of extra fields | * test handling of extra fields | ||||
* | |||||
* @since 1.1 | |||||
*/ | */ | ||||
public void testAddAsFirstExtraField() { | public void testAddAsFirstExtraField() { | ||||
AsiExtraField a = new AsiExtraField(); | AsiExtraField a = new AsiExtraField(); | ||||