diff --git a/WHATSNEW b/WHATSNEW index 8397875bb..342cf5669 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -31,6 +31,9 @@ Other changes: * Project provides new get methods that return copies instead of the live maps of task and type definitions, references and targets. + * Ant is now more lenient with ZIP extra fields and will be able to + read archives that it failed to read in earlier versions. + Changes from Ant 1.8.0RC1 TO Ant 1.8.0 ====================================== diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java index 8f2267a49..fe049c0e8 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Zip.java +++ b/src/main/org/apache/tools/ant/taskdefs/Zip.java @@ -1026,7 +1026,7 @@ public class Zip extends MatchingTask { try { is = zf.getInputStream(ze); zipFile(is, zOut, prefix + name, ze.getTime(), - fromArchive, mode, ze.getExtraFields()); + fromArchive, mode, ze.getExtraFields(true)); } finally { doCompress = oldCompress; FileUtils.close(is); diff --git a/src/main/org/apache/tools/ant/types/resources/ZipResource.java b/src/main/org/apache/tools/ant/types/resources/ZipResource.java index 4e14720c7..8d46631ba 100644 --- a/src/main/org/apache/tools/ant/types/resources/ZipResource.java +++ b/src/main/org/apache/tools/ant/types/resources/ZipResource.java @@ -219,7 +219,7 @@ public class ZipResource extends ArchiveResource { setDirectory(e.isDirectory()); setSize(e.getSize()); setMode(e.getUnixMode()); - extras = e.getExtraFields(); + extras = e.getExtraFields(true); method = e.getMethod(); } diff --git a/src/main/org/apache/tools/zip/ExtraFieldUtils.java b/src/main/org/apache/tools/zip/ExtraFieldUtils.java index d47374744..7f6460ebb 100644 --- a/src/main/org/apache/tools/zip/ExtraFieldUtils.java +++ b/src/main/org/apache/tools/zip/ExtraFieldUtils.java @@ -92,18 +92,19 @@ public class ExtraFieldUtils { /** * Split the array into ExtraFields and populate them with the - * given data as local file data. + * given data as local file data, throwing an exception if the + * data cannot be parsed. * @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); + return parse(data, true, UnparseableExtraField.THROW); } /** * Split the array into ExtraFields and populate them with the - * given data. + * given data, throwing an exception if the data cannot be parsed. * @param data an array of bytes * @param local whether data originates from the local file data * or the central directory @@ -113,14 +114,60 @@ public class ExtraFieldUtils { */ public static ZipExtraField[] parse(byte[] data, boolean local) throws ZipException { + return parse(data, local, UnparseableExtraField.THROW); + } + + /** + * Split the array into ExtraFields and populate them with the + * given data. + * @param data an array of bytes + * @param local whether data originates from the local file data + * or the central directory + * @param onUnparseableData what to do if the extra field data + * cannot be parsed. + * @return an array of ExtraFields + * @throws ZipException on error + * @since Ant 1.8.1 + */ + public static ZipExtraField[] parse(byte[] data, boolean local, + UnparseableExtraField onUnparseableData) + throws ZipException { List v = new ArrayList(); int start = 0; + LOOP: while (start <= data.length - WORD) { ZipShort headerId = new ZipShort(data, start); int length = (new ZipShort(data, start + 2)).getValue(); if (start + WORD + length > data.length) { - throw new ZipException("data starting at " + start - + " is in unknown format"); + switch(onUnparseableData.getKey()) { + case UnparseableExtraField.THROW_KEY: + throw new ZipException("bad extra field starting at " + + start + ". Block length of " + + length + " bytes exceeds remaining" + + " data of " + + (data.length - start - WORD) + + " bytes."); + case UnparseableExtraField.READ_KEY: + UnparseableExtraFieldData field = + new UnparseableExtraFieldData(); + if (local) { + field.parseFromLocalFileData(data, start, + data.length - start); + } else { + field.parseFromCentralDirectoryData(data, start, + data.length - start); + } + v.add(field); + /*FALLTHROUGH*/ + case UnparseableExtraField.SKIP_KEY: + // since we cannot parse the data we must assume + // the extra field consumes the whole rest of the + // available data + break LOOP; + default: + throw new ZipException("unknown UnparseableExtraField key: " + + onUnparseableData.getKey()); + } } try { ZipExtraField ze = createExtraField(headerId); @@ -152,13 +199,19 @@ public class ExtraFieldUtils { * @since 1.1 */ public static byte[] mergeLocalFileDataData(ZipExtraField[] data) { - int sum = WORD * data.length; + final boolean lastIsUnparseableHolder = data.length > 0 + && data[data.length - 1] instanceof UnparseableExtraFieldData; + int regularExtraFieldCount = + lastIsUnparseableHolder ? data.length - 1 : data.length; + + int sum = WORD * regularExtraFieldCount; for (int i = 0; i < data.length; i++) { sum += data[i].getLocalFileDataLength().getValue(); } + byte[] result = new byte[sum]; int start = 0; - for (int i = 0; i < data.length; i++) { + for (int i = 0; i < regularExtraFieldCount; i++) { System.arraycopy(data[i].getHeaderId().getBytes(), 0, result, start, 2); System.arraycopy(data[i].getLocalFileDataLength().getBytes(), @@ -167,6 +220,10 @@ public class ExtraFieldUtils { System.arraycopy(local, 0, result, start + WORD, local.length); start += (local.length + WORD); } + if (lastIsUnparseableHolder) { + byte[] local = data[data.length - 1].getLocalFileDataData(); + System.arraycopy(local, 0, result, start, local.length); + } return result; } @@ -177,13 +234,18 @@ public class ExtraFieldUtils { * @since 1.1 */ public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) { - int sum = WORD * data.length; + final boolean lastIsUnparseableHolder = data.length > 0 + && data[data.length - 1] instanceof UnparseableExtraFieldData; + int regularExtraFieldCount = + lastIsUnparseableHolder ? data.length - 1 : data.length; + + int sum = WORD * regularExtraFieldCount; for (int i = 0; i < data.length; i++) { sum += data[i].getCentralDirectoryLength().getValue(); } byte[] result = new byte[sum]; int start = 0; - for (int i = 0; i < data.length; i++) { + for (int i = 0; i < regularExtraFieldCount; i++) { System.arraycopy(data[i].getHeaderId().getBytes(), 0, result, start, 2); System.arraycopy(data[i].getCentralDirectoryLength().getBytes(), @@ -192,6 +254,60 @@ public class ExtraFieldUtils { System.arraycopy(local, 0, result, start + WORD, local.length); start += (local.length + WORD); } + if (lastIsUnparseableHolder) { + byte[] local = data[data.length - 1].getCentralDirectoryData(); + System.arraycopy(local, 0, result, start, local.length); + } return result; } + + /** + * "enum" for the possible actions to take if the extra field + * cannot be parsed. + */ + public static final class UnparseableExtraField { + /** + * Key for "throw an exception" action. + */ + public static final int THROW_KEY = 0; + /** + * Key for "skip" action. + */ + public static final int SKIP_KEY = 1; + /** + * Key for "read" action. + */ + public static final int READ_KEY = 2; + + /** + * Throw an exception if field cannot be parsed. + */ + public static final UnparseableExtraField THROW + = new UnparseableExtraField(THROW_KEY); + + /** + * Skip the extra field entirely and don't make its data + * available - effectively removing the extra field data. + */ + public static final UnparseableExtraField SKIP + = new UnparseableExtraField(SKIP_KEY); + + /** + * Read the extra field data into an instance of {@link + * UnparseableExtraFieldData UnparseableExtraFieldData}. + */ + public static final UnparseableExtraField READ + = new UnparseableExtraField(READ_KEY); + + private final int key; + + private UnparseableExtraField(int k) { + key = k; + } + + /** + * Key of the action to take. + */ + public int getKey() { return key; } + } } diff --git a/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java b/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java new file mode 100644 index 000000000..ab46f8b4b --- /dev/null +++ b/src/main/org/apache/tools/zip/UnparseableExtraFieldData.java @@ -0,0 +1,115 @@ +/* + * 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; + +/** + * Wrapper for extra field data that doesn't conform to the recommended format of header-tag + size + data. + * + *

The header-id is artificial (and not listed as a know ID in + * {@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT + * APPNOTE.TXT}. Since it isn't used anywhere except to satisfy the + * ZipExtraField contract it shouldn't matter anyway.

+ * @since Ant 1.8.1 + */ +public final class UnparseableExtraFieldData + implements CentralDirectoryParsingZipExtraField { + + private static final ZipShort HEADER_ID = new ZipShort(0xACC1); + + private byte[] localFileData; + private byte[] centralDirectoryData; + + /** + * The Header-ID. + * + * @return a completely arbitrary value that should be ignored. + */ + public ZipShort getHeaderId() { + return HEADER_ID; + } + + /** + * Length of the complete extra field in the local file data. + * + * @return The LocalFileDataLength value + */ + public ZipShort getLocalFileDataLength() { + return new ZipShort(localFileData == null ? 0 : localFileData.length); + } + + /** + * Length of the complete extra field in the central directory. + * + * @return The CentralDirectoryLength value + */ + public ZipShort getCentralDirectoryLength() { + return centralDirectoryData == null + ? getLocalFileDataLength() + : new ZipShort(centralDirectoryData.length); + } + + /** + * The actual data to put into local file data. + * + * @return The LocalFileDataData value + */ + public byte[] getLocalFileDataData() { + return ZipUtil.copy(localFileData); + } + + /** + * The actual data to put into central directory. + * + * @return The CentralDirectoryData value + */ + public byte[] getCentralDirectoryData() { + return centralDirectoryData == null + ? getLocalFileDataData() : ZipUtil.copy(centralDirectoryData); + } + + /** + * Populate data from this array as if it was in local file data. + * + * @param buffer the buffer to read data from + * @param offset offset into buffer to read data + * @param length the length of data + */ + public void parseFromLocalFileData(byte[] buffer, int offset, int length) { + localFileData = new byte[length]; + System.arraycopy(buffer, offset, localFileData, 0, length); + } + + /** + * Populate data from this array as if it was in central directory data. + * + * @param buffer the buffer to read data from + * @param offset offset into buffer to read data + * @param length the length of data + * @exception ZipException on error + */ + public void parseFromCentralDirectoryData(byte[] buffer, int offset, + int length) { + centralDirectoryData = new byte[length]; + System.arraycopy(buffer, offset, centralDirectoryData, 0, length); + if (localFileData == null) { + parseFromLocalFileData(buffer, offset, length); + } + } + +} diff --git a/src/main/org/apache/tools/zip/UnrecognizedExtraField.java b/src/main/org/apache/tools/zip/UnrecognizedExtraField.java index 3ae23d87b..0e4262de8 100644 --- a/src/main/org/apache/tools/zip/UnrecognizedExtraField.java +++ b/src/main/org/apache/tools/zip/UnrecognizedExtraField.java @@ -66,7 +66,7 @@ public class UnrecognizedExtraField * @param data the field data to use */ public void setLocalFileDataData(byte[] data) { - localData = copy(data); + localData = ZipUtil.copy(data); } /** @@ -82,7 +82,7 @@ public class UnrecognizedExtraField * @return the local data */ public byte[] getLocalFileDataData() { - return copy(localData); + return ZipUtil.copy(localData); } /** @@ -98,7 +98,7 @@ public class UnrecognizedExtraField * @param data the data to use */ public void setCentralDirectoryData(byte[] data) { - centralData = copy(data); + centralData = ZipUtil.copy(data); } /** @@ -119,7 +119,7 @@ public class UnrecognizedExtraField */ public byte[] getCentralDirectoryData() { if (centralData != null) { - return copy(centralData); + return ZipUtil.copy(centralData); } return getLocalFileDataData(); } @@ -151,12 +151,4 @@ public class UnrecognizedExtraField } } - private static byte[] copy(byte[] from) { - if (from != null) { - byte[] to = new byte[from.length]; - System.arraycopy(from, 0, to, 0, to.length); - return to; - } - return null; - } } diff --git a/src/main/org/apache/tools/zip/ZipEntry.java b/src/main/org/apache/tools/zip/ZipEntry.java index e22b6cff1..bd6fb1e59 100644 --- a/src/main/org/apache/tools/zip/ZipEntry.java +++ b/src/main/org/apache/tools/zip/ZipEntry.java @@ -18,13 +18,32 @@ package org.apache.tools.zip; +import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; import java.util.zip.ZipException; /** * Extension that adds better handling of extra fields and provides * access to the internal and external file attributes. * + *

The extra data is expected to follow the recommendation of + * {@link http://www.pkware.com/documents/casestudies/APPNOTE.TXT + * APPNOTE.txt}:

+ * + * + *

Any extra data that cannot be parsed by the rules above will be + * consumed as "unparseable" extra data and treated differently by the + * methods of this class. Versions prior to Apache Commons Compress + * 1.1 would have thrown an exception if any attempt was made to read + * or write extra data not conforming to the recommendation.

+ * */ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { @@ -37,6 +56,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { private int platform = PLATFORM_FAT; private long externalAttributes = 0; private LinkedHashMap/**/ extraFields = null; + private UnparseableExtraFieldData unparseableExtra = null; private String name = null; /** @@ -58,7 +78,9 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { super(entry); byte[] extra = entry.getExtra(); if (extra != null) { - setExtraFields(ExtraFieldUtils.parse(extra)); + setExtraFields(ExtraFieldUtils.parse(extra, true, + ExtraFieldUtils + .UnparseableExtraField.READ)); } else { // initializes extra data to an empty byte array setExtra(); @@ -75,7 +97,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { this((java.util.zip.ZipEntry) entry); setInternalAttributes(entry.getInternalAttributes()); setExternalAttributes(entry.getExternalAttributes()); - setExtraFields(entry.getExtraFields()); + setExtraFields(entry.getExtraFields(true)); } /** @@ -93,10 +115,9 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { public Object clone() { ZipEntry e = (ZipEntry) super.clone(); - e.extraFields = extraFields != null ? (LinkedHashMap) extraFields.clone() : null; e.setInternalAttributes(getInternalAttributes()); e.setExternalAttributes(getExternalAttributes()); - e.setExtraFields(getExtraFields()); + e.setExtraFields(getExtraFields(true)); return e; } @@ -194,26 +215,46 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { public void setExtraFields(ZipExtraField[] fields) { extraFields = new LinkedHashMap(); for (int i = 0; i < fields.length; i++) { - extraFields.put(fields[i].getHeaderId(), fields[i]); + if (fields[i] instanceof UnparseableExtraFieldData) { + unparseableExtra = (UnparseableExtraFieldData) fields[i]; + } else { + extraFields.put(fields[i].getHeaderId(), fields[i]); + } } setExtra(); } + /** + * Retrieves all extra fields that have been parsed successfully. + * @return an array of the extra fields + */ + public ZipExtraField[] getExtraFields() { + return getExtraFields(false); + } + /** * Retrieves extra fields. + * @param includeUnparseable whether to also return unparseable + * extra fields as {@link UnparseableExtraFieldData} if such data + * exists. * @return an array of the extra fields * @since 1.1 */ - public ZipExtraField[] getExtraFields() { + public ZipExtraField[] getExtraFields(boolean includeUnparseable) { if (extraFields == null) { - return new ZipExtraField[0]; + return !includeUnparseable || unparseableExtra == null + ? new ZipExtraField[0] + : new ZipExtraField[] { unparseableExtra }; + } + List result = new ArrayList(extraFields.values()); + if (includeUnparseable && unparseableExtra != null) { + result.add(unparseableExtra); } - ZipExtraField[] result = new ZipExtraField[extraFields.size()]; - return (ZipExtraField[]) extraFields.values().toArray(result); + return (ZipExtraField[]) result.toArray(new ZipExtraField[0]); } /** - * Adds an extra fields - replacing an already present extra field + * Adds an extra field - replacing an already present extra field * of the same type. * *

If no extra field of the same type exists, the field will be @@ -222,15 +263,19 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { * @since 1.1 */ public void addExtraField(ZipExtraField ze) { - if (extraFields == null) { - extraFields = new LinkedHashMap(); + if (ze instanceof UnparseableExtraFieldData) { + unparseableExtra = (UnparseableExtraFieldData) ze; + } else { + if (extraFields == null) { + extraFields = new LinkedHashMap(); + } + extraFields.put(ze.getHeaderId(), ze); } - extraFields.put(ze.getHeaderId(), ze); setExtra(); } /** - * Adds an extra fields - replacing an already present extra field + * Adds an extra field - replacing an already present extra field * of the same type. * *

The new extra field will be the first one.

@@ -238,18 +283,22 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { * @since 1.1 */ public void addAsFirstExtraField(ZipExtraField ze) { - LinkedHashMap copy = extraFields; - extraFields = new LinkedHashMap(); - extraFields.put(ze.getHeaderId(), ze); - if (copy != null) { - copy.remove(ze.getHeaderId()); - extraFields.putAll(copy); + if (ze instanceof UnparseableExtraFieldData) { + unparseableExtra = (UnparseableExtraFieldData) ze; + } else { + LinkedHashMap copy = extraFields; + extraFields = new LinkedHashMap(); + extraFields.put(ze.getHeaderId(), ze); + if (copy != null) { + copy.remove(ze.getHeaderId()); + extraFields.putAll(copy); + } } setExtra(); } /** - * Remove an extra fields. + * Remove an extra field. * @param type the type of extra field to remove * @since 1.1 */ @@ -263,6 +312,17 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { setExtra(); } + /** + * Removes unparseable extra field data. + */ + public void removeUnparseableExtraFieldData() { + if (unparseableExtra == null) { + throw new java.util.NoSuchElementException(); + } + unparseableExtra = null; + setExtra(); + } + /** * Looks up an extra field by its header id. * @@ -276,7 +336,18 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { } /** - * Throws an Exception if extra data cannot be parsed into extra fields. + * Looks up extra field data that couldn't be parsed correctly. + * + * @return null if no such field exists. + */ + public UnparseableExtraFieldData getUnparseableExtraFieldData() { + return unparseableExtra; + } + + /** + * Parses the given bytes as extra field data and consumes any + * unparseable data as an {@link UnparseableExtraFieldData} + * instance. * @param extra an array of bytes to be parsed into extra fields * @throws RuntimeException if the bytes cannot be parsed * @since 1.1 @@ -284,10 +355,14 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { */ public void setExtra(byte[] extra) throws RuntimeException { try { - ZipExtraField[] local = ExtraFieldUtils.parse(extra, true); + ZipExtraField[] local = + ExtraFieldUtils.parse(extra, true, + ExtraFieldUtils.UnparseableExtraField.READ); mergeExtraFields(local, true); } catch (Exception e) { - throw new RuntimeException(e.getMessage(), e); + // actually this is not be possible as of Ant 1.8.1 + throw new RuntimeException("Error parsing extra fields for entry: " + + getName() + " - " + e.getMessage(), e); } } @@ -300,7 +375,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { * @since 1.1 */ protected void setExtra() { - super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields())); + super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields(true))); } /** @@ -308,7 +383,9 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { */ public void setCentralDirectoryExtra(byte[] b) { try { - ZipExtraField[] central = ExtraFieldUtils.parse(b, false); + ZipExtraField[] central = + ExtraFieldUtils.parse(b, false, + ExtraFieldUtils.UnparseableExtraField.READ); mergeExtraFields(central, false); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); @@ -331,7 +408,7 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { * @since 1.1 */ public byte[] getCentralDirectoryExtra() { - return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields()); + return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields(true)); } /** @@ -413,7 +490,12 @@ public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable { setExtraFields(f); } else { for (int i = 0; i < f.length; i++) { - ZipExtraField existing = getExtraField(f[i].getHeaderId()); + ZipExtraField existing; + if (f[i] instanceof UnparseableExtraFieldData) { + existing = unparseableExtra; + } else { + existing = getExtraField(f[i].getHeaderId()); + } if (existing == null) { addExtraField(f[i]); } else { diff --git a/src/main/org/apache/tools/zip/ZipUtil.java b/src/main/org/apache/tools/zip/ZipUtil.java new file mode 100644 index 000000000..9d7dcf22c --- /dev/null +++ b/src/main/org/apache/tools/zip/ZipUtil.java @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * Utility class for handling DOS and Java time conversions. + * @since Ant 1.8.1 + */ +public abstract class ZipUtil { + /** + * Create a copy of the given array - or return null if the + * argument is null. + */ + static byte[] copy(byte[] from) { + if (from != null) { + byte[] to = new byte[from.length]; + System.arraycopy(from, 0, to, 0, to.length); + return to; + } + return null; + } + +} \ No newline at end of file diff --git a/src/tests/antunit/taskdefs/unzip-test.xml b/src/tests/antunit/taskdefs/unzip-test.xml index 45a878f54..b2c2105dd 100644 --- a/src/tests/antunit/taskdefs/unzip-test.xml +++ b/src/tests/antunit/taskdefs/unzip-test.xml @@ -59,7 +59,7 @@ > - + diff --git a/src/tests/antunit/taskdefs/zip/Bugzilla-46559.zip b/src/tests/antunit/taskdefs/zip/Bugzilla-46559.zip deleted file mode 100644 index 711402b6d..000000000 Binary files a/src/tests/antunit/taskdefs/zip/Bugzilla-46559.zip and /dev/null differ diff --git a/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java b/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java index 13e07193e..400efd266 100644 --- a/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java +++ b/src/tests/junit/org/apache/tools/zip/ExtraFieldUtilsTest.java @@ -18,6 +18,7 @@ package org.apache.tools.zip; +import java.util.Arrays; import junit.framework.TestCase; /** @@ -78,11 +79,65 @@ public class ExtraFieldUtilsTest extends TestCase implements UnixStat { fail("data should be invalid"); } catch (Exception e) { assertEquals("message", - "data starting at "+(4+aLocal.length)+" is in unknown format", + "bad extra field starting at "+(4 + aLocal.length) + + ". Block length of 1 bytes exceeds remaining data of 0 bytes.", e.getMessage()); } } + public void testParseWithRead() throws Exception { + ZipExtraField[] ze = + ExtraFieldUtils.parse(data, true, + ExtraFieldUtils.UnparseableExtraField.READ); + assertEquals("number of fields", 2, ze.length); + assertTrue("type field 1", ze[0] instanceof AsiExtraField); + assertEquals("mode field 1", 040755, + ((AsiExtraField) ze[0]).getMode()); + assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField); + assertEquals("data length field 2", 1, + ze[1].getLocalFileDataLength().getValue()); + + byte[] data2 = new byte[data.length-1]; + System.arraycopy(data, 0, data2, 0, data2.length); + ze = ExtraFieldUtils.parse(data2, true, + ExtraFieldUtils.UnparseableExtraField.READ); + assertEquals("number of fields", 2, ze.length); + assertTrue("type field 1", ze[0] instanceof AsiExtraField); + assertEquals("mode field 1", 040755, + ((AsiExtraField) ze[0]).getMode()); + assertTrue("type field 2", ze[1] instanceof UnparseableExtraFieldData); + assertEquals("data length field 2", 4, + ze[1].getLocalFileDataLength().getValue()); + byte[] expectedData = new byte[4]; + for (int i = 0; i < 4; i++) { + assertEquals("byte number " + i, + data2[data.length - 5 + i], + ze[1].getLocalFileDataData()[i]); + } + } + + public void testParseWithSkip() throws Exception { + ZipExtraField[] ze = + ExtraFieldUtils.parse(data, true, + ExtraFieldUtils.UnparseableExtraField.SKIP); + assertEquals("number of fields", 2, ze.length); + assertTrue("type field 1", ze[0] instanceof AsiExtraField); + assertEquals("mode field 1", 040755, + ((AsiExtraField) ze[0]).getMode()); + assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField); + assertEquals("data length field 2", 1, + ze[1].getLocalFileDataLength().getValue()); + + byte[] data2 = new byte[data.length-1]; + System.arraycopy(data, 0, data2, 0, data2.length); + ze = ExtraFieldUtils.parse(data2, true, + ExtraFieldUtils.UnparseableExtraField.SKIP); + assertEquals("number of fields", 1, ze.length); + assertTrue("type field 1", ze[0] instanceof AsiExtraField); + assertEquals("mode field 1", 040755, + ((AsiExtraField) ze[0]).getMode()); + } + /** * Test merge methods */ @@ -111,4 +166,30 @@ public class ExtraFieldUtilsTest extends TestCase implements UnixStat { } } + + public void testMergeWithUnparseableData() throws Exception { + ZipExtraField d = new UnparseableExtraFieldData(); + d.parseFromLocalFileData(new byte[] {1, 0, 1, 0}, 0, 4); + byte[] local = + ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] {a, d}); + assertEquals("local length", data.length - 1, local.length); + for (int i = 0; i < local.length; i++) { + assertEquals("local byte " + i, data[i], local[i]); + } + + byte[] dCentral = d.getCentralDirectoryData(); + byte[] data2 = new byte[4 + aLocal.length + dCentral.length]; + System.arraycopy(data, 0, data2, 0, 4 + aLocal.length + 2); + System.arraycopy(dCentral, 0, data2, + 4 + aLocal.length, dCentral.length); + + + byte[] central = + ExtraFieldUtils.mergeCentralDirectoryData(new ZipExtraField[] {a, d}); + assertEquals("central length", data2.length, central.length); + for (int i = 0; i < central.length; i++) { + assertEquals("central byte " + i, data2[i], central[i]); + } + + } }