You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2017/05/11 18:15:28 UTC

[1/6] commons-compress git commit: COMPRESS-391: Persist alignment request

Repository: commons-compress
Updated Branches:
  refs/heads/master ca7ea939e -> fef18a23d


COMPRESS-391: Persist alignment request


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/f2af9f0c
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/f2af9f0c
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/f2af9f0c

Branch: refs/heads/master
Commit: f2af9f0c583ce5d5e294b60ada9e14516f5e92ac
Parents: c312e43
Author: Zbynek Vyskovsky <kv...@gmail.com>
Authored: Wed May 10 20:06:08 2017 -0700
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu May 11 20:04:34 2017 +0200

----------------------------------------------------------------------
 .../compress/archivers/zip/ExtraFieldUtils.java |   2 +-
 .../archivers/zip/PaddingExtraField.java        |  87 ------------
 .../zip/ResourceAlignmentExtraField.java        | 133 +++++++++++++++++++
 .../archivers/zip/ZipArchiveOutputStream.java   |  34 +++--
 .../compress/archivers/zip/ZipFileTest.java     |  42 +++++-
 5 files changed, 195 insertions(+), 103 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/f2af9f0c/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java b/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
index c510efa..14691c4 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
@@ -52,7 +52,7 @@ public class ExtraFieldUtils {
         register(X0016_CertificateIdForCentralDirectory.class);
         register(X0017_StrongEncryptionHeader.class);
         register(X0019_EncryptionRecipientCertificateList.class);
-        register(PaddingExtraField.class);
+        register(ResourceAlignmentExtraField.class);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/f2af9f0c/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
deleted file mode 100644
index fc57b36..0000000
--- a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.util.zip.ZipException;
-
-/**
- * An extra field who's sole purpose is to pad the local file header
- * so that the entry's data starts at a certain position.
- *
- * <p>The actual content of the padding is ignored and not retained
- * when reading a padding field.</p>
- *
- * <p>This enables Commons Compress to create "aligned" archives
- * similar to Android's zipalign command line tool.</p>
- *
- * @since 1.14
- * @see "https://developer.android.com/studio/command-line/zipalign.html"
- * @see ZipArchiveEntry#setAlignment
- */
-public class PaddingExtraField implements ZipExtraField {
-
-    /**
-     * Extra field id used for padding (there is no special value documented,
-     * therefore USHORT_MAX seems to be good choice).
-     */
-    public static final ZipShort ID = new ZipShort(0xffff);
-
-    private int len = 0;
-
-    public PaddingExtraField() {
-    }
-
-    public PaddingExtraField(int len) {
-        this.len = len;
-    }
-
-    @Override
-    public ZipShort getHeaderId() {
-        return ID;
-    }
-
-    @Override
-    public ZipShort getLocalFileDataLength() {
-        return new ZipShort(len);
-    }
-
-    @Override
-    public ZipShort getCentralDirectoryLength() {
-        return ZipShort.ZERO;
-    }
-
-    @Override
-    public byte[] getLocalFileDataData() {
-        return new byte[len];
-    }
-
-    @Override
-    public byte[] getCentralDirectoryData() {
-        return new byte[0];
-    }
-
-    @Override
-    public void parseFromLocalFileData(byte[] buffer, int offset, int length) {
-        len = length;
-    }
-
-    @Override
-    public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) {
-    }
-}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/f2af9f0c/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
new file mode 100644
index 0000000..a9f8748
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
@@ -0,0 +1,133 @@
+/*
+ * 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.util.zip.ZipException;
+
+/**
+ * An extra field who's sole purpose is to align and pad the local file header
+ * so that the entry's data starts at a certain position.
+ *
+ * <p>The padding content of the padding is ignored and not retained
+ * when reading a padding field.</p>
+ *
+ * <p>This enables Commons Compress to create "aligned" archives
+ * similar to Android's zipalign command line tool.</p>
+ *
+ * @since 1.14
+ * @see "https://developer.android.com/studio/command-line/zipalign.html"
+ * @see ZipArchiveEntry#setAlignment
+ */
+public class ResourceAlignmentExtraField implements ZipExtraField {
+
+    /**
+     * Extra field id used for storing alignment and padding.
+     */
+    public static final ZipShort ID = new ZipShort(0xa11e);
+
+    public static final int BASE_SIZE = 2;
+
+    private short alignment;
+
+    private boolean allowMethodChange;
+
+    private int padding = 0;
+
+    public ResourceAlignmentExtraField() {
+    }
+
+    public ResourceAlignmentExtraField(int alignment) {
+        this(alignment, false);
+    }
+
+    public ResourceAlignmentExtraField(int alignment, boolean allowMethodChange) {
+        this(alignment, allowMethodChange, 0);
+    }
+
+    public ResourceAlignmentExtraField(int alignment, boolean allowMethodChange, int padding) {
+        if (alignment < 0 || alignment > 0x7fff)
+            throw new IllegalArgumentException("Alignment must be between 0 and 0x7fff, was: " + alignment);
+        this.alignment = (short) alignment;
+        this.allowMethodChange = allowMethodChange;
+        this.padding = padding;
+    }
+
+    /**
+     * Gets requested alignment.
+     *
+     * @return
+     *      requested alignment.
+     */
+    public short getAlignment() {
+        return alignment;
+    }
+
+    /**
+     * Indicates whether method change is allowed when re-compressing the zip file.
+     *
+     * @return
+     *      true if method change is allowed, false otherwise.
+     */
+    public boolean allowMethodChange() {
+        return allowMethodChange;
+    }
+
+    @Override
+    public ZipShort getHeaderId() {
+        return ID;
+    }
+
+    @Override
+    public ZipShort getLocalFileDataLength() {
+        return new ZipShort(BASE_SIZE + padding);
+    }
+
+    @Override
+    public ZipShort getCentralDirectoryLength() {
+        return new ZipShort(BASE_SIZE);
+    }
+
+    @Override
+    public byte[] getLocalFileDataData() {
+        byte[] content = new byte[2+padding];
+        ZipShort.putShort(alignment | (allowMethodChange ? 0x8000 : 0), content, 0);
+        return content;
+    }
+
+    @Override
+    public byte[] getCentralDirectoryData() {
+        return ZipShort.getBytes(alignment | (allowMethodChange ? 0x8000 : 0));
+    }
+
+    @Override
+    public void parseFromLocalFileData(byte[] buffer, int offset, int length) throws ZipException {
+        parseFromCentralDirectoryData(buffer, offset, length);
+        this.padding = length - BASE_SIZE;
+    }
+
+    @Override
+    public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
+        if (length < 2)
+            throw new ZipException("Too short content for ResourceAlignmentExtraField (0xa11e): " + length);
+        int alignmentValue = ZipShort.getValue(buffer, offset);
+        this.alignment = (short) (alignmentValue&0x7fff);
+        this.allowMethodChange = (alignmentValue&0x8000) != 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/f2af9f0c/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
index 359f92b..5649af3 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
@@ -1042,21 +1042,31 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
 
     private byte[] createLocalFileHeader(final ZipArchiveEntry ze, final ByteBuffer name, final boolean encodable,
                                          final boolean phased, long archiveOffset) throws IOException {
+        ResourceAlignmentExtraField oldAlignmentEx =
+                        (ResourceAlignmentExtraField) ze.getExtraField(ResourceAlignmentExtraField.ID);
+        if (oldAlignmentEx != null)
+            ze.removeExtraField(ResourceAlignmentExtraField.ID);
+
+        int alignment = ze.getAlignment();
+        if (alignment <= 0 && oldAlignmentEx != null) {
+            alignment = oldAlignmentEx.getAlignment();
+        }
+
+        if (alignment > 1 || (oldAlignmentEx != null && !oldAlignmentEx.allowMethodChange())) {
+            int oldLength = LFH_FILENAME_OFFSET +
+                            name.limit() - name.position() +
+                            ze.getLocalFileDataExtra().length;
+
+            int padding = (int) ((-archiveOffset - oldLength - EXTRAFIELD_HEADER_SIZE
+                            - ResourceAlignmentExtraField.BASE_SIZE) &
+                            (alignment - 1));
+            ze.addExtraField(new ResourceAlignmentExtraField(alignment,
+                            oldAlignmentEx != null ? oldAlignmentEx.allowMethodChange() : false, padding));
+        }
+
         byte[] extra = ze.getLocalFileDataExtra();
         final int nameLen = name.limit() - name.position();
         int len = LFH_FILENAME_OFFSET + nameLen + extra.length;
-        int alignment = ze.getAlignment();
-        if (alignment > 1 && ((archiveOffset + len) & (alignment - 1)) != 0) {
-            int padding = (int) ((-archiveOffset - len - EXTRAFIELD_HEADER_SIZE) & (alignment - 1));
-            ZipExtraField pex = (PaddingExtraField) ze.getExtraField(PaddingExtraField.ID);
-            if (pex != null) {
-                padding += pex.getLocalFileDataLength().getValue() + EXTRAFIELD_HEADER_SIZE;
-            }
-            // will overwrite an existing PaddingExtraField
-            ze.addExtraField(new PaddingExtraField(padding));
-            extra = ze.getLocalFileDataExtra();
-            len = LFH_FILENAME_OFFSET + nameLen + extra.length;
-        }
         final byte[] buf = new byte[len];
 
         System.arraycopy(LFH_SIG,  0, buf, LFH_SIG_OFFSET, WORD);

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/f2af9f0c/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
index 98d6dcc..6efaba4 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
@@ -485,9 +485,16 @@ public class ZipFileTest {
             ZipArchiveEntry storedEntry2 = new ZipArchiveEntry("stored2.txt");
             storedEntry2.setMethod(ZipEntry.STORED);
             storedEntry2.setAlignment(1024);
-            storedEntry2.addExtraField(new PaddingExtraField(123));
+            storedEntry2.addExtraField(new ResourceAlignmentExtraField(1));
             zipOutput.putArchiveEntry(storedEntry2);
-            zipOutput.write("Hello pre-aligned Stored\n".getBytes(Charset.forName("UTF-8")));
+            zipOutput.write("Hello overload-alignment Stored\n".getBytes(Charset.forName("UTF-8")));
+            zipOutput.closeArchiveEntry();
+
+            ZipArchiveEntry storedEntry3 = new ZipArchiveEntry("stored3.txt");
+            storedEntry3.setMethod(ZipEntry.STORED);
+            storedEntry3.addExtraField(new ResourceAlignmentExtraField(1024));
+            zipOutput.putArchiveEntry(storedEntry3);
+            zipOutput.write("Hello copy-alignment Stored\n".getBytes(Charset.forName("UTF-8")));
             zipOutput.closeArchiveEntry();
 
         }
@@ -496,28 +503,57 @@ public class ZipFileTest {
                         Arrays.copyOfRange(zipContent.array(), 0, (int)zipContent.size())
         ))) {
             ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
+            ResourceAlignmentExtraField inflatedAlignmentEx =
+                            (ResourceAlignmentExtraField)inflatedEntry.getExtraField(ResourceAlignmentExtraField.ID);
             assertNotEquals(-1L, inflatedEntry.getCompressedSize());
             assertNotEquals(-1L, inflatedEntry.getSize());
             assertEquals(0L, inflatedEntry.getDataOffset()%1024);
+            assertNotNull(inflatedAlignmentEx);
+            assertEquals(1024, inflatedAlignmentEx.getAlignment());
+            assertFalse(inflatedAlignmentEx.allowMethodChange());
             try (InputStream stream = zf.getInputStream(inflatedEntry)) {
                 Assert.assertEquals("Hello Deflated\n",
                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
             }
             ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
+            ResourceAlignmentExtraField storedAlignmentEx =
+                            (ResourceAlignmentExtraField)storedEntry.getExtraField(ResourceAlignmentExtraField.ID);
             assertNotEquals(-1L, storedEntry.getCompressedSize());
             assertNotEquals(-1L, storedEntry.getSize());
             assertEquals(0L, storedEntry.getDataOffset()%1024);
+            assertNotNull(storedAlignmentEx);
+            assertEquals(1024, storedAlignmentEx.getAlignment());
+            assertFalse(storedAlignmentEx.allowMethodChange());
             try (InputStream stream = zf.getInputStream(storedEntry)) {
                 Assert.assertEquals("Hello Stored\n",
                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
             }
 
             ZipArchiveEntry storedEntry2 = zf.getEntry("stored2.txt");
+            ResourceAlignmentExtraField stored2AlignmentEx =
+                            (ResourceAlignmentExtraField)storedEntry2.getExtraField(ResourceAlignmentExtraField.ID);
             assertNotEquals(-1L, storedEntry2.getCompressedSize());
             assertNotEquals(-1L, storedEntry2.getSize());
             assertEquals(0L, storedEntry2.getDataOffset()%1024);
+            assertNotNull(stored2AlignmentEx);
+            assertEquals(1024, stored2AlignmentEx.getAlignment());
+            assertFalse(stored2AlignmentEx.allowMethodChange());
             try (InputStream stream = zf.getInputStream(storedEntry2)) {
-                Assert.assertEquals("Hello pre-aligned Stored\n",
+                Assert.assertEquals("Hello overload-alignment Stored\n",
+                                new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
+            }
+
+            ZipArchiveEntry storedEntry3 = zf.getEntry("stored3.txt");
+            ResourceAlignmentExtraField stored3AlignmentEx =
+                            (ResourceAlignmentExtraField)storedEntry3.getExtraField(ResourceAlignmentExtraField.ID);
+            assertNotEquals(-1L, storedEntry3.getCompressedSize());
+            assertNotEquals(-1L, storedEntry3.getSize());
+            assertEquals(0L, storedEntry3.getDataOffset()%1024);
+            assertNotNull(stored3AlignmentEx);
+            assertEquals(1024, stored3AlignmentEx.getAlignment());
+            assertFalse(stored3AlignmentEx.allowMethodChange());
+            try (InputStream stream = zf.getInputStream(storedEntry3)) {
+                Assert.assertEquals("Hello copy-alignment Stored\n",
                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
             }
         }


[5/6] commons-compress git commit: deal with existing PaddingExtraFields when calculating the alignment

Posted by bo...@apache.org.
deal with existing PaddingExtraFields when calculating the alignment


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/702f27a8
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/702f27a8
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/702f27a8

Branch: refs/heads/master
Commit: 702f27a8779fa77a2ea0a1b6cc6e1e4d678d4fbb
Parents: e3e137d
Author: Stefan Bodewig <bo...@apache.org>
Authored: Wed May 10 09:55:52 2017 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu May 11 20:04:34 2017 +0200

----------------------------------------------------------------------
 .../archivers/zip/PaddingExtraField.java         |  2 +-
 .../archivers/zip/ZipArchiveOutputStream.java    |  9 +++++++--
 .../compress/archivers/zip/ZipFileTest.java      | 19 ++++++++++++++++++-
 3 files changed, 26 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/702f27a8/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
index 28df2e6..590cc93 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
@@ -35,7 +35,7 @@ public class PaddingExtraField implements ZipExtraField {
      * Extra field id used for padding (there is no special value documented,
      * therefore USHORT_MAX seems to be good choice).
      */
-    private static final ZipShort ID = new ZipShort(0xffff);
+    public static final ZipShort ID = new ZipShort(0xffff);
 
     private int len = 0;
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/702f27a8/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
index 7161301..359f92b 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
@@ -1044,13 +1044,18 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
                                          final boolean phased, long archiveOffset) throws IOException {
         byte[] extra = ze.getLocalFileDataExtra();
         final int nameLen = name.limit() - name.position();
-        int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
+        int len = LFH_FILENAME_OFFSET + nameLen + extra.length;
         int alignment = ze.getAlignment();
         if (alignment > 1 && ((archiveOffset + len) & (alignment - 1)) != 0) {
             int padding = (int) ((-archiveOffset - len - EXTRAFIELD_HEADER_SIZE) & (alignment - 1));
+            ZipExtraField pex = (PaddingExtraField) ze.getExtraField(PaddingExtraField.ID);
+            if (pex != null) {
+                padding += pex.getLocalFileDataLength().getValue() + EXTRAFIELD_HEADER_SIZE;
+            }
+            // will overwrite an existing PaddingExtraField
             ze.addExtraField(new PaddingExtraField(padding));
             extra = ze.getLocalFileDataExtra();
-            len += EXTRAFIELD_HEADER_SIZE + padding;
+            len = LFH_FILENAME_OFFSET + nameLen + extra.length;
         }
         final byte[] buf = new byte[len];
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/702f27a8/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
index 0c94368..98d6dcc 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
@@ -482,6 +482,14 @@ public class ZipFileTest {
             zipOutput.write("Hello Stored\n".getBytes(Charset.forName("UTF-8")));
             zipOutput.closeArchiveEntry();
 
+            ZipArchiveEntry storedEntry2 = new ZipArchiveEntry("stored2.txt");
+            storedEntry2.setMethod(ZipEntry.STORED);
+            storedEntry2.setAlignment(1024);
+            storedEntry2.addExtraField(new PaddingExtraField(123));
+            zipOutput.putArchiveEntry(storedEntry2);
+            zipOutput.write("Hello pre-aligned Stored\n".getBytes(Charset.forName("UTF-8")));
+            zipOutput.closeArchiveEntry();
+
         }
 
         try (ZipFile zf = new ZipFile(new SeekableInMemoryByteChannel(
@@ -498,11 +506,20 @@ public class ZipFileTest {
             ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
             assertNotEquals(-1L, storedEntry.getCompressedSize());
             assertNotEquals(-1L, storedEntry.getSize());
-            assertEquals(0L, inflatedEntry.getDataOffset()%1024);
+            assertEquals(0L, storedEntry.getDataOffset()%1024);
             try (InputStream stream = zf.getInputStream(storedEntry)) {
                 Assert.assertEquals("Hello Stored\n",
                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
             }
+
+            ZipArchiveEntry storedEntry2 = zf.getEntry("stored2.txt");
+            assertNotEquals(-1L, storedEntry2.getCompressedSize());
+            assertNotEquals(-1L, storedEntry2.getSize());
+            assertEquals(0L, storedEntry2.getDataOffset()%1024);
+            try (InputStream stream = zf.getInputStream(storedEntry2)) {
+                Assert.assertEquals("Hello pre-aligned Stored\n",
+                                new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
+            }
         }
     }
 


[4/6] commons-compress git commit: COMPRESS-391 implement padding as ZipExtraField class

Posted by bo...@apache.org.
COMPRESS-391 implement padding as ZipExtraField class


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/e3e137df
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/e3e137df
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/e3e137df

Branch: refs/heads/master
Commit: e3e137df87f35b4249133478b77c4222f8e530a8
Parents: bd64dc7
Author: Stefan Bodewig <bo...@apache.org>
Authored: Tue May 9 09:27:16 2017 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu May 11 20:04:34 2017 +0200

----------------------------------------------------------------------
 .../compress/archivers/zip/ExtraFieldUtils.java |  1 +
 .../archivers/zip/PaddingExtraField.java        | 82 ++++++++++++++++++++
 .../compress/archivers/zip/ZipArchiveEntry.java |  5 +-
 .../archivers/zip/ZipArchiveOutputStream.java   | 31 ++------
 .../compress/archivers/zip/ZipShort.java        |  2 +
 .../compress/archivers/zip/ZipFileTest.java     |  5 +-
 6 files changed, 96 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e3e137df/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java b/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
index a21c858..c510efa 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
@@ -52,6 +52,7 @@ public class ExtraFieldUtils {
         register(X0016_CertificateIdForCentralDirectory.class);
         register(X0017_StrongEncryptionHeader.class);
         register(X0019_EncryptionRecipientCertificateList.class);
+        register(PaddingExtraField.class);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e3e137df/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
new file mode 100644
index 0000000..28df2e6
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
@@ -0,0 +1,82 @@
+/*
+ * 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.util.zip.ZipException;
+
+/**
+ * An extra field who's sole purpose is to pad the local file header
+ * so that the entry's data starts at a certain position.
+ *
+ * <p>The actual content of the padding is ignored and not retained
+ * when reading a padding field.</p>
+ *
+ * @since 1.14
+ */
+public class PaddingExtraField implements ZipExtraField {
+
+    /**
+     * Extra field id used for padding (there is no special value documented,
+     * therefore USHORT_MAX seems to be good choice).
+     */
+    private static final ZipShort ID = new ZipShort(0xffff);
+
+    private int len = 0;
+
+    public PaddingExtraField() {
+    }
+
+    public PaddingExtraField(int len) {
+        this.len = len;
+    }
+
+    @Override
+    public ZipShort getHeaderId() {
+        return ID;
+    }
+
+    @Override
+    public ZipShort getLocalFileDataLength() {
+        return new ZipShort(len);
+    }
+
+    @Override
+    public ZipShort getCentralDirectoryLength() {
+        return ZipShort.ZERO;
+    }
+
+    @Override
+    public byte[] getLocalFileDataData() {
+        return new byte[len];
+    }
+
+    @Override
+    public byte[] getCentralDirectoryData() {
+        return new byte[0];
+    }
+
+    @Override
+    public void parseFromLocalFileData(byte[] buffer, int offset, int length) {
+        len = length;
+    }
+
+    @Override
+    public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) {
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e3e137df/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
index b626ea6..ffcb953 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
@@ -340,8 +340,9 @@ public class ZipArchiveEntry extends java.util.zip.ZipEntry
      *      requested alignment, 0 for default.
      */
     public void setAlignment(int alignment) {
-        if ((alignment & (alignment - 1)) != 0) {
-            throw new IllegalArgumentException("Invalid value for alignment, must be power of two: " + alignment);
+        if ((alignment & (alignment - 1)) != 0 || alignment > 0xffff) {
+            throw new IllegalArgumentException("Invalid value for alignment, must be power of two and no bigger than "
+                + 0xffff + " but is " + alignment);
         }
         this.alignment = alignment;
     }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e3e137df/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
index 72e3d5b..7161301 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
@@ -143,16 +143,10 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
     public static final int EFS_FLAG = GeneralPurposeBit.UFT8_NAMES_FLAG;
 
     /**
-     * Size of the extra field header (id + length).
+     * Size of an extra field field header (id + length).
      */
     public static final int EXTRAFIELD_HEADER_SIZE = 4;
 
-    /**
-     * Extra field id used for padding (there is no special value documented,
-     * therefore USHORT_MAX seems to be good choice).
-     */
-    public static final int EXTRAFIELD_PADDING_ID = 0xffff;
-
     private static final byte[] EMPTY = new byte[0];
 
     /**
@@ -1048,14 +1042,15 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
 
     private byte[] createLocalFileHeader(final ZipArchiveEntry ze, final ByteBuffer name, final boolean encodable,
                                          final boolean phased, long archiveOffset) throws IOException {
-        final byte[] extra = ze.getLocalFileDataExtra();
+        byte[] extra = ze.getLocalFileDataExtra();
         final int nameLen = name.limit() - name.position();
         int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
-        int padding = 0;
         int alignment = ze.getAlignment();
         if (alignment > 1 && ((archiveOffset + len) & (alignment - 1)) != 0) {
-            padding = (int) ((-archiveOffset - len - EXTRAFIELD_HEADER_SIZE) & (alignment - 1));
-            len += EXTRAFIELD_HEADER_SIZE+padding;
+            int padding = (int) ((-archiveOffset - len - EXTRAFIELD_HEADER_SIZE) & (alignment - 1));
+            ze.addExtraField(new PaddingExtraField(padding));
+            extra = ze.getLocalFileDataExtra();
+            len += EXTRAFIELD_HEADER_SIZE + padding;
         }
         final byte[] buf = new byte[len];
 
@@ -1108,14 +1103,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
         // file name length
         putShort(nameLen, buf, LFH_FILENAME_LENGTH_OFFSET);
 
-        int totalExtra = extra.length + (padding > 0 ? padding + EXTRAFIELD_HEADER_SIZE : 0);
-        if (totalExtra > 0xffff) {
-            throw new IOException("Too much data for extra fields and padding"+
-                            ", extra="+extra.length+
-                            ", padding="+padding);
-        }
         // extra field length
-        putShort(totalExtra, buf, LFH_EXTRA_LENGTH_OFFSET);
+        putShort(extra.length, buf, LFH_EXTRA_LENGTH_OFFSET);
 
         // file name
         System.arraycopy( name.array(), name.arrayOffset(), buf, LFH_FILENAME_OFFSET, nameLen);
@@ -1123,12 +1112,6 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
         // extra fields
         System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, extra.length);
 
-        // padding
-        if (padding > 0) {
-            putShort(EXTRAFIELD_PADDING_ID, buf, LFH_FILENAME_OFFSET + nameLen + extra.length);
-            putShort(padding, buf, LFH_FILENAME_OFFSET + nameLen + extra.length + 2);
-        }
-
         return buf;
     }
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e3e137df/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
index 7d182f8..65acf21 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
@@ -27,6 +27,8 @@ import org.apache.commons.compress.utils.ByteUtils;
  * @Immutable
  */
 public final class ZipShort implements Cloneable, Serializable {
+    public static final ZipShort ZERO = new ZipShort(0);
+
     private static final long serialVersionUID = 1L;
 
     private final int value;

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e3e137df/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
index 5b7b900..0c94368 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
@@ -509,16 +509,13 @@ public class ZipFileTest {
     /**
      * Test too big alignment, resulting into exceeding extra field limit.
      */
-    @Test(expected = IOException.class)
+    @Test(expected = IllegalArgumentException.class)
     public void testEntryAlignmentExceed() throws Exception {
         SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel();
         try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
             ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
             inflatedEntry.setMethod(ZipEntry.STORED);
             inflatedEntry.setAlignment(0x20000);
-            zipOutput.putArchiveEntry(inflatedEntry);
-            zipOutput.write("Hello Stored\n".getBytes(Charset.forName("UTF-8")));
-            zipOutput.closeArchiveEntry();
         }
     }
 


[6/6] commons-compress git commit: COMPRESS-391 some tweaks

Posted by bo...@apache.org.
COMPRESS-391 some tweaks


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/fef18a23
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/fef18a23
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/fef18a23

Branch: refs/heads/master
Commit: fef18a23d3b09025e0ddc3733334a4587c3af712
Parents: f2af9f0
Author: Stefan Bodewig <bo...@apache.org>
Authored: Thu May 11 20:15:10 2017 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu May 11 20:15:10 2017 +0200

----------------------------------------------------------------------
 .../archivers/zip/ResourceAlignmentExtraField.java  | 16 ++++++++++------
 .../compress/archivers/zip/ZipArchiveEntry.java     |  2 ++
 .../archivers/zip/ZipArchiveOutputStream.java       | 16 ++++++----------
 .../compress/archivers/zip/ZipExtraField.java       |  6 ++++++
 .../commons/compress/archivers/zip/ZipShort.java    |  4 ++++
 5 files changed, 28 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/fef18a23/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
index a9f8748..79e20f6 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ResourceAlignmentExtraField.java
@@ -44,6 +44,8 @@ public class ResourceAlignmentExtraField implements ZipExtraField {
 
     public static final int BASE_SIZE = 2;
 
+    private static final int ALLOW_METHOD_MESSAGE_CHANGE_FLAG = 0x8000;
+
     private short alignment;
 
     private boolean allowMethodChange;
@@ -106,14 +108,15 @@ public class ResourceAlignmentExtraField implements ZipExtraField {
 
     @Override
     public byte[] getLocalFileDataData() {
-        byte[] content = new byte[2+padding];
-        ZipShort.putShort(alignment | (allowMethodChange ? 0x8000 : 0), content, 0);
+        byte[] content = new byte[BASE_SIZE + padding];
+        ZipShort.putShort(alignment | (allowMethodChange ? ALLOW_METHOD_MESSAGE_CHANGE_FLAG : 0),
+                          content, 0);
         return content;
     }
 
     @Override
     public byte[] getCentralDirectoryData() {
-        return ZipShort.getBytes(alignment | (allowMethodChange ? 0x8000 : 0));
+        return ZipShort.getBytes(alignment | (allowMethodChange ? ALLOW_METHOD_MESSAGE_CHANGE_FLAG : 0));
     }
 
     @Override
@@ -124,10 +127,11 @@ public class ResourceAlignmentExtraField implements ZipExtraField {
 
     @Override
     public void parseFromCentralDirectoryData(byte[] buffer, int offset, int length) throws ZipException {
-        if (length < 2)
+        if (length < BASE_SIZE) {
             throw new ZipException("Too short content for ResourceAlignmentExtraField (0xa11e): " + length);
+        }
         int alignmentValue = ZipShort.getValue(buffer, offset);
-        this.alignment = (short) (alignmentValue&0x7fff);
-        this.allowMethodChange = (alignmentValue&0x8000) != 0;
+        this.alignment = (short) (alignmentValue & (ALLOW_METHOD_MESSAGE_CHANGE_FLAG - 1));
+        this.allowMethodChange = (alignmentValue & ALLOW_METHOD_MESSAGE_CHANGE_FLAG) != 0;
     }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/fef18a23/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
index ffcb953..69e4990 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
@@ -328,6 +328,7 @@ public class ZipArchiveEntry extends java.util.zip.ZipEntry
      *
      * @return
      *      alignment for this entry.
+     * @since 1.14
      */
     protected int getAlignment() {
         return this.alignment;
@@ -338,6 +339,7 @@ public class ZipArchiveEntry extends java.util.zip.ZipEntry
      *
      * @param alignment
      *      requested alignment, 0 for default.
+     * @since 1.14
      */
     public void setAlignment(int alignment) {
         if ((alignment & (alignment - 1)) != 0 || alignment > 0xffff) {

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/fef18a23/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
index 5649af3..c667356 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
@@ -142,11 +142,6 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
     @Deprecated
     public static final int EFS_FLAG = GeneralPurposeBit.UFT8_NAMES_FLAG;
 
-    /**
-     * Size of an extra field field header (id + length).
-     */
-    public static final int EXTRAFIELD_HEADER_SIZE = 4;
-
     private static final byte[] EMPTY = new byte[0];
 
     /**
@@ -1043,9 +1038,10 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
     private byte[] createLocalFileHeader(final ZipArchiveEntry ze, final ByteBuffer name, final boolean encodable,
                                          final boolean phased, long archiveOffset) throws IOException {
         ResourceAlignmentExtraField oldAlignmentEx =
-                        (ResourceAlignmentExtraField) ze.getExtraField(ResourceAlignmentExtraField.ID);
-        if (oldAlignmentEx != null)
+            (ResourceAlignmentExtraField) ze.getExtraField(ResourceAlignmentExtraField.ID);
+        if (oldAlignmentEx != null) {
             ze.removeExtraField(ResourceAlignmentExtraField.ID);
+        }
 
         int alignment = ze.getAlignment();
         if (alignment <= 0 && oldAlignmentEx != null) {
@@ -1057,16 +1053,16 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
                             name.limit() - name.position() +
                             ze.getLocalFileDataExtra().length;
 
-            int padding = (int) ((-archiveOffset - oldLength - EXTRAFIELD_HEADER_SIZE
+            int padding = (int) ((-archiveOffset - oldLength - ZipExtraField.EXTRAFIELD_HEADER_SIZE
                             - ResourceAlignmentExtraField.BASE_SIZE) &
                             (alignment - 1));
             ze.addExtraField(new ResourceAlignmentExtraField(alignment,
                             oldAlignmentEx != null ? oldAlignmentEx.allowMethodChange() : false, padding));
         }
 
-        byte[] extra = ze.getLocalFileDataExtra();
+        final byte[] extra = ze.getLocalFileDataExtra();
         final int nameLen = name.limit() - name.position();
-        int len = LFH_FILENAME_OFFSET + nameLen + extra.length;
+        final int len = LFH_FILENAME_OFFSET + nameLen + extra.length;
         final byte[] buf = new byte[len];
 
         System.arraycopy(LFH_SIG,  0, buf, LFH_SIG_OFFSET, WORD);

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/fef18a23/src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java
index 3149774..2c44b2a 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java
@@ -32,6 +32,12 @@ import java.util.zip.ZipException;
  */
 public interface ZipExtraField {
     /**
+     * Size of an extra field field header (id + length).
+     * @since 1.14
+     */
+    int EXTRAFIELD_HEADER_SIZE = 4;
+
+    /**
      * The Header-ID.
      *
      * @return The HeaderId value

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/fef18a23/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
index 65acf21..ccb5094 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipShort.java
@@ -27,6 +27,10 @@ import org.apache.commons.compress.utils.ByteUtils;
  * @Immutable
  */
 public final class ZipShort implements Cloneable, Serializable {
+    /**
+     * ZipShort with a value of 0.
+     * @since 1.14
+     */
     public static final ZipShort ZERO = new ZipShort(0);
 
     private static final long serialVersionUID = 1L;


[3/6] commons-compress git commit: COMPRESS-391: Allow alignment on zip content

Posted by bo...@apache.org.
COMPRESS-391: Allow alignment on zip content


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/bd64dc7a
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/bd64dc7a
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/bd64dc7a

Branch: refs/heads/master
Commit: bd64dc7abee36984e830c740588990739b71cc93
Parents: ca7ea93
Author: Zbynek Vyskovsky <kv...@gmail.com>
Authored: Thu May 4 20:44:44 2017 -0700
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu May 11 20:04:34 2017 +0200

----------------------------------------------------------------------
 .../compress/archivers/zip/ZipArchiveEntry.java | 24 +++++++
 .../archivers/zip/ZipArchiveOutputStream.java   | 39 +++++++++--
 .../compress/archivers/zip/ZipFileTest.java     | 71 ++++++++++++++++++++
 3 files changed, 130 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/bd64dc7a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
index f769cf5..b626ea6 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
@@ -85,6 +85,7 @@ public class ZipArchiveEntry extends java.util.zip.ZipEntry
     private int platform = PLATFORM_FAT;
     private int rawFlag;
     private long externalAttributes = 0;
+    private int alignment = 0;
     private ZipExtraField[] extraFields;
     private UnparseableExtraFieldData unparseableExtra = null;
     private String name = null;
@@ -323,6 +324,29 @@ public class ZipArchiveEntry extends java.util.zip.ZipEntry
     }
 
     /**
+     * Gets currently configured alignment.
+     *
+     * @return
+     *      alignment for this entry.
+     */
+    protected int getAlignment() {
+        return this.alignment;
+    }
+
+    /**
+     * Sets alignment for this entry.
+     *
+     * @param alignment
+     *      requested alignment, 0 for default.
+     */
+    public void setAlignment(int alignment) {
+        if ((alignment & (alignment - 1)) != 0) {
+            throw new IllegalArgumentException("Invalid value for alignment, must be power of two: " + alignment);
+        }
+        this.alignment = alignment;
+    }
+
+    /**
      * Replaces all currently attached extra fields with the new array.
      * @param fields an array of extra fields
      */

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/bd64dc7a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
index e3066b0..72e3d5b 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
@@ -142,6 +142,17 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
     @Deprecated
     public static final int EFS_FLAG = GeneralPurposeBit.UFT8_NAMES_FLAG;
 
+    /**
+     * Size of the extra field header (id + length).
+     */
+    public static final int EXTRAFIELD_HEADER_SIZE = 4;
+
+    /**
+     * Extra field id used for padding (there is no special value documented,
+     * therefore USHORT_MAX seems to be good choice).
+     */
+    public static final int EXTRAFIELD_PADDING_ID = 0xffff;
+
     private static final byte[] EMPTY = new byte[0];
 
     /**
@@ -1026,8 +1037,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
             addUnicodeExtraFields(ze, encodable, name);
         }
 
-        final byte[] localHeader = createLocalFileHeader(ze, name, encodable, phased);
         final long localHeaderStart = streamCompressor.getTotalBytesWritten();
+        final byte[] localHeader = createLocalFileHeader(ze, name, encodable, phased, localHeaderStart);
         offsets.put(ze, localHeaderStart);
         entry.localDataStart = localHeaderStart + LFH_CRC_OFFSET; // At crc offset
         writeCounted(localHeader);
@@ -1036,10 +1047,16 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
 
 
     private byte[] createLocalFileHeader(final ZipArchiveEntry ze, final ByteBuffer name, final boolean encodable,
-                                         final boolean phased)  {
+                                         final boolean phased, long archiveOffset) throws IOException {
         final byte[] extra = ze.getLocalFileDataExtra();
         final int nameLen = name.limit() - name.position();
-        final int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
+        int len= LFH_FILENAME_OFFSET + nameLen + extra.length;
+        int padding = 0;
+        int alignment = ze.getAlignment();
+        if (alignment > 1 && ((archiveOffset + len) & (alignment - 1)) != 0) {
+            padding = (int) ((-archiveOffset - len - EXTRAFIELD_HEADER_SIZE) & (alignment - 1));
+            len += EXTRAFIELD_HEADER_SIZE+padding;
+        }
         final byte[] buf = new byte[len];
 
         System.arraycopy(LFH_SIG,  0, buf, LFH_SIG_OFFSET, WORD);
@@ -1091,13 +1108,27 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
         // file name length
         putShort(nameLen, buf, LFH_FILENAME_LENGTH_OFFSET);
 
+        int totalExtra = extra.length + (padding > 0 ? padding + EXTRAFIELD_HEADER_SIZE : 0);
+        if (totalExtra > 0xffff) {
+            throw new IOException("Too much data for extra fields and padding"+
+                            ", extra="+extra.length+
+                            ", padding="+padding);
+        }
         // extra field length
-        putShort(extra.length, buf, LFH_EXTRA_LENGTH_OFFSET);
+        putShort(totalExtra, buf, LFH_EXTRA_LENGTH_OFFSET);
 
         // file name
         System.arraycopy( name.array(), name.arrayOffset(), buf, LFH_FILENAME_OFFSET, nameLen);
 
+        // extra fields
         System.arraycopy(extra, 0, buf, LFH_FILENAME_OFFSET + nameLen, extra.length);
+
+        // padding
+        if (padding > 0) {
+            putShort(EXTRAFIELD_PADDING_ID, buf, LFH_FILENAME_OFFSET + nameLen + extra.length);
+            putShort(padding, buf, LFH_FILENAME_OFFSET + nameLen + extra.length + 2);
+        }
+
         return buf;
     }
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/bd64dc7a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
index 8455bd7..5b7b900 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
@@ -28,6 +28,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -460,6 +461,76 @@ public class ZipFileTest {
         }
     }
 
+    /**
+     * Test entries alignment.
+     */
+    @Test
+    public void testEntryAlignment() throws Exception {
+        SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel();
+        try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
+            ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
+            inflatedEntry.setMethod(ZipEntry.DEFLATED);
+            inflatedEntry.setAlignment(1024);
+            zipOutput.putArchiveEntry(inflatedEntry);
+            zipOutput.write("Hello Deflated\n".getBytes(Charset.forName("UTF-8")));
+            zipOutput.closeArchiveEntry();
+
+            ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt");
+            storedEntry.setMethod(ZipEntry.STORED);
+            storedEntry.setAlignment(1024);
+            zipOutput.putArchiveEntry(storedEntry);
+            zipOutput.write("Hello Stored\n".getBytes(Charset.forName("UTF-8")));
+            zipOutput.closeArchiveEntry();
+
+        }
+
+        try (ZipFile zf = new ZipFile(new SeekableInMemoryByteChannel(
+                        Arrays.copyOfRange(zipContent.array(), 0, (int)zipContent.size())
+        ))) {
+            ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
+            assertNotEquals(-1L, inflatedEntry.getCompressedSize());
+            assertNotEquals(-1L, inflatedEntry.getSize());
+            assertEquals(0L, inflatedEntry.getDataOffset()%1024);
+            try (InputStream stream = zf.getInputStream(inflatedEntry)) {
+                Assert.assertEquals("Hello Deflated\n",
+                                new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
+            }
+            ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
+            assertNotEquals(-1L, storedEntry.getCompressedSize());
+            assertNotEquals(-1L, storedEntry.getSize());
+            assertEquals(0L, inflatedEntry.getDataOffset()%1024);
+            try (InputStream stream = zf.getInputStream(storedEntry)) {
+                Assert.assertEquals("Hello Stored\n",
+                                new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
+            }
+        }
+    }
+
+    /**
+     * Test too big alignment, resulting into exceeding extra field limit.
+     */
+    @Test(expected = IOException.class)
+    public void testEntryAlignmentExceed() throws Exception {
+        SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel();
+        try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
+            ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
+            inflatedEntry.setMethod(ZipEntry.STORED);
+            inflatedEntry.setAlignment(0x20000);
+            zipOutput.putArchiveEntry(inflatedEntry);
+            zipOutput.write("Hello Stored\n".getBytes(Charset.forName("UTF-8")));
+            zipOutput.closeArchiveEntry();
+        }
+    }
+
+    /**
+     * Test non power of 2 alignment.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidAlignment() throws Exception {
+        ZipArchiveEntry entry = new ZipArchiveEntry("dummy");
+        entry.setAlignment(3);
+    }
+
     private void assertAllReadMethods(byte[] expected, ZipFile zipFile, ZipArchiveEntry entry) {
         // simple IOUtil read
         try (InputStream stream = zf.getInputStream(entry)) {


[2/6] commons-compress git commit: COMPRESS-391 add pointer to zipalign

Posted by bo...@apache.org.
COMPRESS-391 add pointer to zipalign


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/c312e43b
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/c312e43b
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/c312e43b

Branch: refs/heads/master
Commit: c312e43b03d1e18fd65908621a0ce885ae1c5fed
Parents: 702f27a
Author: Stefan Bodewig <bo...@apache.org>
Authored: Wed May 10 10:02:58 2017 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Thu May 11 20:04:34 2017 +0200

----------------------------------------------------------------------
 .../commons/compress/archivers/zip/PaddingExtraField.java       | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/c312e43b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
index 590cc93..fc57b36 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/PaddingExtraField.java
@@ -27,7 +27,12 @@ import java.util.zip.ZipException;
  * <p>The actual content of the padding is ignored and not retained
  * when reading a padding field.</p>
  *
+ * <p>This enables Commons Compress to create "aligned" archives
+ * similar to Android's zipalign command line tool.</p>
+ *
  * @since 1.14
+ * @see "https://developer.android.com/studio/command-line/zipalign.html"
+ * @see ZipArchiveEntry#setAlignment
  */
 public class PaddingExtraField implements ZipExtraField {