You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ju...@apache.org on 2013/01/08 01:13:18 UTC
svn commit: r1430102 - in /commons/proper/compress/trunk/src:
main/java/org/apache/commons/compress/archivers/zip/
test/java/org/apache/commons/compress/archivers/zip/ test/resources/
Author: julius
Date: Tue Jan 8 00:13:18 2013
New Revision: 1430102
URL: http://svn.apache.org/viewvc?rev=1430102&view=rev
Log:
COMPRESS-211 - handle zip extra field 0x7875 - Info Zip New Unix Extra Field
Added:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/X7875_NewUnix.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/X7875_NewUnixTest.java
commons/proper/compress/trunk/src/test/resources/COMPRESS-211_uid_gid_zip_test.zip (with props)
Modified:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java
Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java?rev=1430102&r1=1430101&r2=1430102&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java Tue Jan 8 00:13:18 2013
@@ -40,6 +40,7 @@ public class ExtraFieldUtils {
static {
implementations = new ConcurrentHashMap<ZipShort, Class<?>>();
register(AsiExtraField.class);
+ register(X7875_NewUnix.class);
register(JarMarker.class);
register(UnicodePathExtraField.class);
register(UnicodeCommentExtraField.class);
Added: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/X7875_NewUnix.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/X7875_NewUnix.java?rev=1430102&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/X7875_NewUnix.java (added)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/X7875_NewUnix.java Tue Jan 8 00:13:18 2013
@@ -0,0 +1,339 @@
+/*
+ * 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.commons.compress.archivers.zip;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.zip.ZipException;
+
+import static org.apache.commons.compress.archivers.zip.ZipUtil.reverse;
+import static org.apache.commons.compress.archivers.zip.ZipUtil.signedByteToUnsignedInt;
+import static org.apache.commons.compress.archivers.zip.ZipUtil.unsignedIntToSignedByte;
+
+/**
+ * An extra field that stores UNIX UID/GID data (owner & group ownership) for a given
+ * zip entry. We're using the field definition given in Info-Zip's source archive:
+ * zip-3.0.tar.gz/proginfo/extrafld.txt
+ * <p/>
+ * <pre>
+ * Value Size Description
+ * ----- ---- -----------
+ * 0x7875 Short tag for this extra block type ("ux")
+ * TSize Short total data size for this block
+ * Version 1 byte version of this extra field, currently 1
+ * UIDSize 1 byte Size of UID field
+ * UID Variable UID for this entry (little endian)
+ * GIDSize 1 byte Size of GID field
+ * GID Variable GID for this entry (little endian)
+ * </pre>
+ */
+public class X7875_NewUnix implements ZipExtraField, Cloneable, Serializable {
+ private static final ZipShort HEADER_ID = new ZipShort(0x7875);
+ private static final BigInteger ONE_THOUSAND = BigInteger.valueOf(1000);
+ private static final long serialVersionUID = 1L;
+
+ private int version = 1; // always '1' according to current info-zip spec.
+
+ // BigInteger helps us with little-endian / big-endian conversions.
+ // (thanks to BigInteger.toByteArray() and a reverse() method we created).
+ // Also, the spec theoretically allows UID/GID up to 255 bytes long!
+ private BigInteger uid;
+ private BigInteger gid;
+
+ /**
+ * Constructor for X7875_NewUnix.
+ */
+ public X7875_NewUnix() {
+ reset();
+ }
+
+ /**
+ * The Header-ID.
+ *
+ * @return the value for the header id for this extrafield
+ */
+ public ZipShort getHeaderId() {
+ return HEADER_ID;
+ }
+
+ /**
+ * Gets the UID as a long. UID is typically a 32 bit unsigned
+ * value on most UNIX systems, so we return a long to avoid
+ * integer overflow into the negatives in case values above
+ * and including 2^31 are being used.
+ *
+ * @return the UID value.
+ */
+ public long getUID() { return ZipUtil.bigToLong(uid); }
+
+ /**
+ * Gets the GID as a long. GID is typically a 32 bit unsigned
+ * value on most UNIX systems, so we return a long to avoid
+ * integer overflow into the negatives in case values above
+ * and including 2^31 are being used.
+ *
+ * @return the GID value.
+ */
+ public long getGID() { return ZipUtil.bigToLong(gid); }
+
+ /**
+ * Sets the UID.
+ *
+ * @param l UID value to set on this extra field.
+ */
+ public void setUID(long l) {
+ this.uid = ZipUtil.longToBig(l);
+ }
+
+ /**
+ * Sets the GID.
+ *
+ * @param l GID value to set on this extra field.
+ */
+ public void setGID(long l) {
+ this.gid = ZipUtil.longToBig(l);
+ }
+
+ /**
+ * Length of the extra field in the local file data - without
+ * Header-ID or length specifier.
+ *
+ * @return a <code>ZipShort</code> for the length of the data of this extra field
+ */
+ public ZipShort getLocalFileDataLength() {
+ int uidSize = trimLeadingZeroesForceMinLength(uid.toByteArray()).length;
+ int gidSize = trimLeadingZeroesForceMinLength(gid.toByteArray()).length;
+
+ // The 3 comes from: version=1 + uidsize=1 + gidsize=1
+ return new ZipShort(3 + uidSize + gidSize);
+ }
+
+ /**
+ * Length of the extra field in the central directory data - without
+ * Header-ID or length specifier.
+ *
+ * @return a <code>ZipShort</code> for the length of the data of this extra field
+ */
+ public ZipShort getCentralDirectoryLength() {
+ return getLocalFileDataLength(); // No different than local version.
+ }
+
+ /**
+ * The actual data to put into local file data - without Header-ID
+ * or length specifier.
+ *
+ * @return get the data
+ */
+ public byte[] getLocalFileDataData() {
+ byte[] uidBytes = uid.toByteArray();
+ byte[] gidBytes = gid.toByteArray();
+
+ // BigInteger might prepend a leading-zero to force a positive representation
+ // (e.g., so that the sign-bit is set to zero). We need to remove that
+ // before sending the number over the wire.
+ uidBytes = trimLeadingZeroesForceMinLength(uidBytes);
+ gidBytes = trimLeadingZeroesForceMinLength(gidBytes);
+
+ // Couldn't bring myself to just call getLocalFileDataLength() when we've
+ // already got the arrays right here. Yeah, yeah, I know, premature
+ // optimization is the root of all...
+ //
+ // The 3 comes from: version=1 + uidsize=1 + gidsize=1
+ byte[] data = new byte[3 + uidBytes.length + gidBytes.length];
+
+ // reverse() switches byte array from big-endian to little-endian.
+ reverse(uidBytes);
+ reverse(gidBytes);
+
+ int pos = 0;
+ data[pos++] = unsignedIntToSignedByte(version);
+ data[pos++] = unsignedIntToSignedByte(uidBytes.length);
+ System.arraycopy(uidBytes, 0, data, pos, uidBytes.length);
+ pos += uidBytes.length;
+ data[pos++] = unsignedIntToSignedByte(gidBytes.length);
+ System.arraycopy(gidBytes, 0, data, pos, gidBytes.length);
+ return data;
+ }
+
+ /**
+ * The actual data to put into central directory data - without Header-ID
+ * or length specifier.
+ *
+ * @return get the data
+ */
+ public byte[] getCentralDirectoryData() {
+ return getLocalFileDataData();
+ }
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ *
+ * @param data an array of bytes
+ * @param offset the start offset
+ * @param length the number of bytes in the array from offset
+ * @throws java.util.zip.ZipException on error
+ */
+ public void parseFromLocalFileData(
+ byte[] data, int offset, int length
+ ) throws ZipException {
+ reset();
+ this.version = signedByteToUnsignedInt(data[offset++]);
+ int uidSize = signedByteToUnsignedInt(data[offset++]);
+ byte[] uidBytes = new byte[uidSize];
+ System.arraycopy(data, offset, uidBytes, 0, uidSize);
+ offset += uidSize;
+ this.uid = new BigInteger(1, reverse(uidBytes)); // sign-bit forced positive
+
+ int gidSize = signedByteToUnsignedInt(data[offset++]);
+ byte[] gidBytes = new byte[gidSize];
+ System.arraycopy(data, offset, gidBytes, 0, gidSize);
+ this.gid = new BigInteger(1, reverse(gidBytes)); // sign-bit forced positive
+ }
+
+ /**
+ * Doesn't do anything special since this class always uses the
+ * same data in central directory and local file data.
+ */
+ public void parseFromCentralDirectoryData(
+ byte[] buffer, int offset, int length
+ ) throws ZipException {
+ reset();
+ parseFromLocalFileData(buffer, offset, length);
+ }
+
+ /**
+ * Reset state back to newly constructed state. Helps us make sure
+ * parse() calls always generate clean results.
+ */
+ private void reset() {
+ // Typical UID/GID of the first non-root user created on a unix system.
+ uid = ONE_THOUSAND;
+ gid = ONE_THOUSAND;
+ }
+
+ /**
+ * Returns a String representation of this class useful for
+ * debugging purposes.
+ *
+ * @return A String representation of this class useful for
+ * debugging purposes.
+ */
+ public String toString() {
+ return "0x7875 Zip Extra Field: UID=" + uid + " GID=" + gid;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof X7875_NewUnix) {
+ X7875_NewUnix xf = (X7875_NewUnix) o;
+ if (version == xf.version) {
+ // The BigInteger==BigInteger clause handles the case where both are null.
+ if (uid == xf.uid || (uid != null && uid.equals(xf.uid))) {
+ return gid == xf.gid || (gid != null && gid.equals(xf.gid));
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = (-1234567 * version);
+ if (uid != null) {
+ hc ^= uid.hashCode();
+ }
+ if (gid != null) {
+ hc ^= gid.hashCode();
+ }
+ return hc;
+ }
+
+ /**
+ * Not really for external usage, but marked "package" visibility
+ * to help us JUnit it. Trims a byte array of leading zeroes while
+ * also enforcing a minimum length, and thus it really trims AND pads
+ * at the same time.
+ *
+ * @param array byte[] array to trim & pad.
+ * @return trimmed & padded byte[] array.
+ */
+ static byte[] trimLeadingZeroesForceMinLength(byte[] array) {
+ if (array == null) {
+ return array;
+ }
+
+ int pos = 0;
+ for (byte b : array) {
+ if (b == 0) {
+ pos++;
+ } else {
+ break;
+ }
+ }
+
+ /*
+
+ I agonized over my choice of MIN_LENGTH=1. Here's the situation:
+ InfoZip (the tool I am using to test interop) always sets these
+ to length=4. And so a UID of 0 (typically root) for example is
+ encoded as {4,0,0,0,0} (len=4, 32 bits of zero), when it could just
+ as easily be encoded as {1,0} (len=1, 8 bits of zero) according to
+ the spec.
+
+ In the end I decided on MIN_LENGTH=1 for four reasons:
+
+ 1.) We are adhering to the spec as far as I can tell, and so
+ a consumer that cannot parse this is broken.
+
+ 2.) Fundamentally, zip files are about shrinking things, so
+ let's save a few bytes per entry while we can.
+
+ 3.) Of all the people creating zip files using commons-
+ compress, how many care about UNIX UID/GID attributes
+ of the files they store? (e.g., I am probably thinking
+ way too hard about this and no one cares!)
+
+ 4.) InfoZip's tool, even though it carefully stores every UID/GID
+ for every file zipped on a unix machine (by default) currently
+ appears unable to ever restore UID/GID.
+ unzip -X has no effect on my machine, even when run as root!!!!
+
+ And thus it is decided: MIN_LENGTH=1.
+
+ If anyone runs into interop problems from this, feel free to set
+ it to MIN_LENGTH=4 at some future time, and then we will behave
+ exactly like InfoZip (requires changes to unit tests, though).
+
+ And I am sorry that the time you spent reading this comment is now
+ gone and you can never have it back.
+
+ */
+ final int MIN_LENGTH = 1;
+
+ byte[] trimmedArray = new byte[Math.max(MIN_LENGTH, array.length - pos)];
+ int startPos = trimmedArray.length - (array.length - pos);
+ System.arraycopy(array, pos, trimmedArray, startPos, trimmedArray.length - startPos);
+ return trimmedArray;
+ }
+}
Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java?rev=1430102&r1=1430101&r2=1430102&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipUtil.java Tue Jan 8 00:13:18 2013
@@ -18,6 +18,7 @@
package org.apache.commons.compress.archivers.zip;
import java.io.IOException;
+import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import java.util.zip.CRC32;
@@ -71,9 +72,6 @@ public abstract class ZipUtil {
* Assumes a negative integer really is a positive integer that
* has wrapped around and re-creates the original value.
*
- * <p>This methods is no longer used as of Apache Commons Compress
- * 1.3</p>
- *
* @param i the value to treat as unsigned int.
* @return the unsigned int as a long.
*/
@@ -86,6 +84,96 @@ public abstract class ZipUtil {
}
/**
+ * Reverses a byte[] array. Reverses in-place (thus provided array is
+ * mutated), but also returns same for convenience.
+ *
+ * @param array to reverse (mutated in-place, but also returned for
+ * convenience).
+ *
+ * @return the reversed array (mutated in-place, but also returned for
+ * convenience).
+ */
+ public static byte[] reverse(final byte[] array) {
+ final int z = array.length - 1; // position of last element
+ for (int i = 0; i < array.length / 2; i++) {
+ byte x = array[i];
+ array[i] = array[z - i];
+ array[z - i] = x;
+ }
+ return array;
+ }
+
+ /**
+ * Converts a BigInteger into a long, and blows up
+ * (NumberFormatException) if the BigInteger is too big.
+ *
+ * @param big BigInteger to convert.
+ * @return long representation of the BigInteger.
+ */
+ static long bigToLong(BigInteger big) {
+ if (big.bitLength() <= 63) { // bitLength() doesn't count the sign bit.
+ return big.longValue();
+ } else {
+ throw new NumberFormatException("The BigInteger cannot fit inside a 64 bit java long: [" + big + "]");
+ }
+ }
+
+ /**
+ * <p>
+ * Converts a long into a BigInteger. Negative numbers between -1 and
+ * -2^31 are treated as unsigned 32 bit (e.g., positive) integers.
+ * Negative numbers below -2^31 cause an IllegalArgumentException
+ * to be thrown.
+ * </p>
+ *
+ * @param l long to convert to BigInteger.
+ * @return BigInteger representation of the provided long.
+ */
+ static BigInteger longToBig(long l) {
+ if (l < Integer.MIN_VALUE) {
+ throw new IllegalArgumentException("Negative longs < -2^31 not permitted: [" + l + "]");
+ } else if (l < 0 && l >= Integer.MIN_VALUE) {
+ // If someone passes in a -2, they probably mean 4294967294
+ // (For example, Unix UID/GID's are 32 bit unsigned.)
+ l = ZipUtil.adjustToLong((int) l);
+ }
+ return BigInteger.valueOf(l);
+ }
+
+ /**
+ * Converts a signed byte into an unsigned integer representation
+ * (e.g., -1 becomes 255).
+ *
+ * @param b byte to convert to int
+ * @return int representation of the provided byte
+ */
+ public static int signedByteToUnsignedInt(byte b) {
+ if (b >= 0) {
+ return b;
+ } else {
+ return 256 + b;
+ }
+ }
+
+ /**
+ * Converts an unsigned integer to a signed byte (e.g., 255 becomes -1).
+ *
+ * @param i integer to convert to byte
+ * @return byte representation of the provided int
+ * @throws IllegalArgumentException if the provided integer is not inside the range [0,255].
+ */
+ public static byte unsignedIntToSignedByte(int i) {
+ if (i > 255 || i < 0) {
+ throw new IllegalArgumentException("Can only convert non-negative integers between [0,255] to byte: [" + i + "]");
+ }
+ if (i < 128) {
+ return (byte) i;
+ } else {
+ return (byte) (i - 256);
+ }
+ }
+
+ /**
* Convert a DOS date/time field to a Date object.
*
* @param zipDosTime contains the stored DOS time.
Added: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/X7875_NewUnixTest.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/X7875_NewUnixTest.java?rev=1430102&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/X7875_NewUnixTest.java (added)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/X7875_NewUnixTest.java Tue Jan 8 00:13:18 2013
@@ -0,0 +1,223 @@
+/*
+ * 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.commons.compress.archivers.zip;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.zip.ZipException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class X7875_NewUnixTest {
+
+ private final static ZipShort X7875 = new ZipShort(0x7875);
+
+ private X7875_NewUnix xf;
+
+ @Before
+ public void before() {
+ xf = new X7875_NewUnix();
+ }
+
+
+ @Test
+ public void testSampleFile() throws Exception {
+ URL zip = getClass().getResource("/COMPRESS-211_uid_gid_zip_test.zip");
+ File archive = new File(new URI(zip.toString()));
+ ZipFile zf = null;
+
+ try {
+ zf = new ZipFile(archive);
+ Enumeration<ZipArchiveEntry> en = zf.getEntries();
+
+ // We expect EVERY entry of this zip file (dir & file) to
+ // contain extra field 0x7875.
+ while (en.hasMoreElements()) {
+
+ ZipArchiveEntry zae = en.nextElement();
+ String name = zae.getName();
+ X7875_NewUnix xf = (X7875_NewUnix) zae.getExtraField(X7875);
+
+ // The directory entry in the test zip file is uid/gid 1000.
+ long expected = 1000;
+ if (name.contains("uid555_gid555")) {
+ expected = 555;
+ } else if (name.contains("uid5555_gid5555")) {
+ expected = 5555;
+ } else if (name.contains("uid55555_gid55555")) {
+ expected = 55555;
+ } else if (name.contains("uid555555_gid555555")) {
+ expected = 555555;
+ } else if (name.contains("min_unix")) {
+ expected = 0;
+ } else if (name.contains("max_unix")) {
+ // 2^32-2 was the biggest UID/GID I could create on my linux!
+ // (December 2012, linux kernel 3.4)
+ expected = 0x100000000L - 2;
+ }
+ assertEquals(expected, xf.getUID());
+ assertEquals(expected, xf.getGID());
+ }
+ } finally {
+ if (zf != null) {
+ zf.close();
+ }
+ }
+ }
+
+ @Test
+ public void testGetHeaderId() {
+ assertEquals(X7875, xf.getHeaderId());
+ }
+
+ @Test
+ public void testMisc() throws Exception {
+ assertTrue(xf.toString().startsWith("0x7875 Zip Extra Field"));
+ Object o = xf.clone();
+ assertEquals(o.hashCode(), xf.hashCode());
+ assertTrue(xf.equals(o));
+ }
+
+ @Test
+ public void testTrimLeadingZeroesForceMinLength4() throws ZipException {
+ final byte[] NULL = null;
+ final byte[] EMPTY = new byte[0];
+ final byte[] ONE_ZERO = {0};
+ final byte[] TWO_ZEROES = {0, 0};
+ final byte[] FOUR_ZEROES = {0, 0, 0, 0};
+ final byte[] SEQUENCE = {1, 2, 3};
+ final byte[] SEQUENCE_LEADING_ZERO = {0, 1, 2, 3};
+ final byte[] SEQUENCE_LEADING_ZEROES = {0, 0, 0, 0, 0, 0, 0, 1, 2, 3};
+ final byte[] TRAILING_ZERO = {1, 2, 3, 0};
+ final byte[] PADDING_ZERO = {0, 1, 2, 3, 0};
+ final byte[] SEQUENCE6 = {1, 2, 3, 4, 5, 6};
+ final byte[] SEQUENCE6_LEADING_ZERO = {0, 1, 2, 3, 4, 5, 6};
+
+ assertTrue(NULL == trimTest(NULL));
+ assertTrue(Arrays.equals(ONE_ZERO, trimTest(EMPTY)));
+ assertTrue(Arrays.equals(ONE_ZERO, trimTest(ONE_ZERO)));
+ assertTrue(Arrays.equals(ONE_ZERO, trimTest(TWO_ZEROES)));
+ assertTrue(Arrays.equals(ONE_ZERO, trimTest(FOUR_ZEROES)));
+ assertTrue(Arrays.equals(SEQUENCE, trimTest(SEQUENCE)));
+ assertTrue(Arrays.equals(SEQUENCE, trimTest(SEQUENCE_LEADING_ZERO)));
+ assertTrue(Arrays.equals(SEQUENCE, trimTest(SEQUENCE_LEADING_ZEROES)));
+ assertTrue(Arrays.equals(TRAILING_ZERO, trimTest(TRAILING_ZERO)));
+ assertTrue(Arrays.equals(TRAILING_ZERO, trimTest(PADDING_ZERO)));
+ assertTrue(Arrays.equals(SEQUENCE6, trimTest(SEQUENCE6)));
+ assertTrue(Arrays.equals(SEQUENCE6, trimTest(SEQUENCE6_LEADING_ZERO)));
+ }
+
+ private static byte[] trimTest(byte[] b) { return X7875_NewUnix.trimLeadingZeroesForceMinLength(b); }
+
+ @Test
+ public void testParseReparse() throws ZipException {
+
+ // Version=1, Len=1, zero, Len=1, zero.
+ final byte[] ZERO_UID_GID = {1, 1, 0, 1, 0};
+
+ // Version=1, Len=1, one, Len=1, one
+ final byte[] ONE_UID_GID = {1, 1, 1, 1, 1};
+
+ // Version=1, Len=2, one thousand, Len=2, one thousand
+ final byte[] ONE_THOUSAND_UID_GID = {1, 2, -24, 3, 2, -24, 3};
+
+ // (2^32 - 2). I guess they avoid (2^32 - 1) since it's identical to -1 in
+ // two's complement, and -1 often has a special meaning.
+ final byte[] UNIX_MAX_UID_GID = {1, 4, -2, -1, -1, -1, 4, -2, -1, -1, -1};
+
+ // Version=1, Len=5, 2^32, Len=5, 2^32 + 1
+ // Esoteric test: can we handle 40 bit numbers?
+ final byte[] LENGTH_5 = {1, 5, 0, 0, 0, 0, 1, 5, 1, 0, 0, 0, 1};
+
+ // Version=1, Len=8, 2^63 - 2, Len=8, 2^63 - 1
+ // Esoteric test: can we handle 64 bit numbers?
+ final byte[] LENGTH_8 = {1, 8, -2, -1, -1, -1, -1, -1, -1, 127, 8, -1, -1, -1, -1, -1, -1, -1, 127};
+
+ final long TWO_TO_32 = 0x100000000L;
+ final long MAX = TWO_TO_32 - 2;
+
+ parseReparse(0, 0, ZERO_UID_GID, 0, 0);
+ parseReparse(1, 1, ONE_UID_GID, 1, 1);
+ parseReparse(1000, 1000, ONE_THOUSAND_UID_GID, 1000, 1000);
+ parseReparse(MAX, MAX, UNIX_MAX_UID_GID, MAX, MAX);
+ parseReparse(-2, -2, UNIX_MAX_UID_GID, MAX, MAX);
+ parseReparse(TWO_TO_32, TWO_TO_32 + 1, LENGTH_5, TWO_TO_32, TWO_TO_32 + 1);
+ parseReparse(Long.MAX_VALUE - 1, Long.MAX_VALUE, LENGTH_8, Long.MAX_VALUE - 1, Long.MAX_VALUE);
+
+ // We never emit this, but we should be able to parse it:
+ final byte[] SPURIOUS_ZEROES_1 = {1, 4, -1, 0, 0, 0, 4, -128, 0, 0, 0};
+ final byte[] EXPECTED_1 = {1, 1, -1, 1, -128};
+ xf.parseFromLocalFileData(SPURIOUS_ZEROES_1, 0, SPURIOUS_ZEROES_1.length);
+
+ assertEquals(255, xf.getUID());
+ assertEquals(128, xf.getGID());
+ assertTrue(Arrays.equals(EXPECTED_1, xf.getLocalFileDataData()));
+
+ final byte[] SPURIOUS_ZEROES_2 = {1, 4, -1, -1, 0, 0, 4, 1, 2, 0, 0};
+ final byte[] EXPECTED_2 = {1, 2, -1, -1, 2, 1, 2};
+ xf.parseFromLocalFileData(SPURIOUS_ZEROES_2, 0, SPURIOUS_ZEROES_2.length);
+
+ assertEquals(65535, xf.getUID());
+ assertEquals(513, xf.getGID());
+ assertTrue(Arrays.equals(EXPECTED_2, xf.getLocalFileDataData()));
+ }
+
+
+ private void parseReparse(
+ final long uid,
+ final long gid,
+ final byte[] expected,
+ final long expectedUID,
+ final long expectedGID
+ ) throws ZipException {
+ xf.setUID(uid);
+ xf.setGID(gid);
+ assertEquals(expected.length, xf.getLocalFileDataLength().getValue());
+ byte[] result = xf.getLocalFileDataData();
+ assertTrue(Arrays.equals(expected, result));
+
+ // And now we re-parse:
+ xf.parseFromLocalFileData(result, 0, result.length);
+
+ // Did uid/gid change from re-parse? They shouldn't!
+ assertEquals(expectedUID, xf.getUID());
+ assertEquals(expectedGID, xf.getGID());
+
+ // Do the same as above, but with Central Directory data:
+ xf.setUID(uid);
+ xf.setGID(gid);
+ assertEquals(expected.length, xf.getCentralDirectoryLength().getValue());
+ result = xf.getCentralDirectoryData();
+ assertTrue(Arrays.equals(expected, result));
+
+ // And now we re-parse:
+ xf.parseFromCentralDirectoryData(result, 0, result.length);
+
+ // Did uid/gid change from 2nd re-parse? They shouldn't!
+ assertEquals(expectedUID, xf.getUID());
+ assertEquals(expectedGID, xf.getGID());
+ }
+}
Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java?rev=1430102&r1=1430101&r2=1430102&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java (original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ZipUtilTest.java Tue Jan 8 00:13:18 2013
@@ -18,11 +18,13 @@
package org.apache.commons.compress.archivers.zip;
+import junit.framework.TestCase;
+
+import java.math.BigInteger;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
-import junit.framework.TestCase;
-
public class ZipUtilTest extends TestCase {
private Date time;
@@ -83,4 +85,126 @@ public class ZipUtilTest extends TestCas
byte[] b2 = ZipUtil.toDosTime(0); // get the same time
assertEquals(b10,b2[0]); // first byte should still be the same
}
+
+ public void testReverse() {
+ byte[][] bTest = new byte[6][];
+ bTest[0] = new byte[]{};
+ bTest[1] = new byte[]{1};
+ bTest[2] = new byte[]{1, 2};
+ bTest[3] = new byte[]{1, 2, 3};
+ bTest[4] = new byte[]{1, 2, 3, 4};
+ bTest[5] = new byte[]{1, 2, 3, 4, 5};
+
+ byte[][] rTest = new byte[6][];
+ rTest[0] = new byte[]{};
+ rTest[1] = new byte[]{1};
+ rTest[2] = new byte[]{2, 1};
+ rTest[3] = new byte[]{3, 2, 1};
+ rTest[4] = new byte[]{4, 3, 2, 1};
+ rTest[5] = new byte[]{5, 4, 3, 2, 1};
+
+ assertEquals("test and result arrays are same length", bTest.length, rTest.length);
+
+ for (int i = 0; i < bTest.length; i++) {
+ byte[] result = ZipUtil.reverse(bTest[i]);
+ assertTrue("reverse mutates in-place", bTest[i] == result);
+ assertTrue("reverse actually reverses", Arrays.equals(rTest[i], result));
+ }
+ }
+
+ public void testBigToLong() {
+ BigInteger big1 = BigInteger.valueOf(1);
+ BigInteger big2 = BigInteger.valueOf(Long.MAX_VALUE);
+ BigInteger big3 = BigInteger.valueOf(Long.MIN_VALUE);
+
+ assertEquals(1L, ZipUtil.bigToLong(big1));
+ assertEquals(Long.MAX_VALUE, ZipUtil.bigToLong(big2));
+ assertEquals(Long.MIN_VALUE, ZipUtil.bigToLong(big3));
+
+ BigInteger big4 = big2.add(big1);
+ try {
+ ZipUtil.bigToLong(big4);
+ fail("Should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // All is good.
+ }
+
+ BigInteger big5 = big3.subtract(big1);
+ try {
+ ZipUtil.bigToLong(big5);
+ fail("ZipUtil.bigToLong(BigInteger) should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // All is good.
+ }
+ }
+
+ public void testLongToBig() {
+ long l0 = 0;
+ long l1 = 1;
+ long l2 = -1;
+ long l3 = Integer.MIN_VALUE;
+ long l4 = Long.MAX_VALUE;
+ long l5 = Long.MIN_VALUE;
+
+ BigInteger big0 = ZipUtil.longToBig(l0);
+ BigInteger big1 = ZipUtil.longToBig(l1);
+ BigInteger big2 = ZipUtil.longToBig(l2);
+ BigInteger big3 = ZipUtil.longToBig(l3);
+ BigInteger big4 = ZipUtil.longToBig(l4);
+
+ assertEquals(0, big0.longValue());
+ assertEquals(1, big1.longValue());
+ assertEquals(0xFFFFFFFFL, big2.longValue());
+ assertEquals(0x80000000L, big3.longValue());
+ assertEquals(Long.MAX_VALUE, big4.longValue());
+
+ try {
+ ZipUtil.longToBig(l5);
+ fail("ZipUtil.longToBig(long) should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+
+ }
+ }
+
+ public void testSignedByteToUnsignedInt() {
+ // Yay, we can completely test all possible input values in this case!
+ int expectedVal = 128;
+ for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+ byte b = (byte) i;
+ assertEquals(expectedVal, ZipUtil.signedByteToUnsignedInt(b));
+ expectedVal++;
+ if (expectedVal == 256) {
+ expectedVal = 0;
+ }
+ }
+ }
+
+ public void testUnsignedIntToSignedByte() {
+ int unsignedVal = 128;
+ for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+ byte expectedVal = (byte) i;
+ assertEquals(expectedVal, ZipUtil.unsignedIntToSignedByte(unsignedVal));
+ unsignedVal++;
+ if (unsignedVal == 256) {
+ unsignedVal = 0;
+ }
+ }
+
+ try {
+ ZipUtil.unsignedIntToSignedByte(-1);
+ fail("ZipUtil.unsignedIntToSignedByte(-1) should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // All is good.
+ }
+
+ try {
+ ZipUtil.unsignedIntToSignedByte(256);
+ fail("ZipUtil.unsignedIntToSignedByte(256) should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // All is good.
+ }
+
+ }
+
+
}
Added: commons/proper/compress/trunk/src/test/resources/COMPRESS-211_uid_gid_zip_test.zip
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/COMPRESS-211_uid_gid_zip_test.zip?rev=1430102&view=auto
==============================================================================
Binary file - no diff available.
Propchange: commons/proper/compress/trunk/src/test/resources/COMPRESS-211_uid_gid_zip_test.zip
------------------------------------------------------------------------------
svn:mime-type = application/octet-stream