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 <bode...@apache.org> Authored: Tue May 9 09:27:16 2017 +0200 Committer: Stefan Bodewig <bode...@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(); } }