COMPRESS-271 content checksum for LZ4 frames

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

Branch: refs/heads/master
Commit: 68fa68928bf3d431209dc0342126b9023de02c93
Parents: 9dc8d30
Author: Stefan Bodewig <bode...@apache.org>
Authored: Thu Jan 26 06:33:44 2017 +0100
Committer: Stefan Bodewig <bode...@apache.org>
Committed: Thu Jan 26 06:33:44 2017 +0100

----------------------------------------------------------------------
 .../lz4/FramedLZ4CompressorInputStream.java     | 15 ++++++++++---
 .../lz4/FramedLZ4CompressorOutputStream.java    | 17 ++++++++++-----
 .../lz4/FramedLZ4CompressorInputStreamTest.java | 22 ++++++++++++++++++++
 .../lz4/FramedLZ4CompressorRoundtripTest.java   |  8 ++++---
 4 files changed, 51 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/68fa6892/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStream.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStream.java
 
b/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStream.java
index 7cbd688..cc8ffed 100644
--- 
a/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStream.java
@@ -125,6 +125,9 @@ public class FramedLZ4CompressorInputStream extends 
CompressorInputStream {
                 r = readOnce(b, off, len);
             }
         }
+        if (expectContentChecksum && r != -1) {
+            contentHash.update(b, off, r);
+        }
         return r;
     }
 
@@ -213,11 +216,17 @@ public class FramedLZ4CompressorInputStream extends 
CompressorInputStream {
 
     private void verifyContentChecksum() throws IOException {
         if (expectContentChecksum) {
-            int skipped = (int) IOUtils.skip(in, 4);
-            count(skipped);
-            if (4 != skipped) {
+            byte[] checksum = new byte[4];
+            int read = IOUtils.readFully(in, checksum);
+            count(read);
+            if (4 != read) {
                 throw new IOException("Premature end of stream while reading 
content checksum");
             }
+            long expectedHash = contentHash.getValue();
+            if (expectedHash != ByteUtils.fromLittleEndian(checksum)) {
+                throw new IOException("content checksum mismatch.");
+            }
+            contentHash.reset();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/68fa6892/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorOutputStream.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorOutputStream.java
 
b/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorOutputStream.java
index b7ad0d0..525e05b 100644
--- 
a/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorOutputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorOutputStream.java
@@ -40,7 +40,7 @@ public class FramedLZ4CompressorOutputStream extends 
CompressorOutputStream {
     /*
      * TODO before releasing 1.14:
      *
-     * + xxhash32 checksum creation for content, blocks
+     * + xxhash32 checksum creation for blocks
      * + block dependence
      */
 
@@ -89,6 +89,7 @@ public class FramedLZ4CompressorOutputStream extends 
CompressorOutputStream {
      */
     public static class Parameters {
         private final BlockSize blockSize;
+        private final boolean withContentChecksum;
 
         /**
          * The default parameters of 4M block size, enabled content
@@ -96,18 +97,20 @@ public class FramedLZ4CompressorOutputStream extends 
CompressorOutputStream {
          *
          * <p>This matches the defaults of the lz4 command line utility.</p>
          */
-        public static Parameters DEFAULT = new Parameters(BlockSize.M4);
+        public static Parameters DEFAULT = new Parameters(BlockSize.M4, true);
 
         /**
          * Sets up custom parameters for the LZ4 stream.
          * @param blockSize the size of a single block.
+         * @param withContentChecksum whether to write a content checksum
          */
-        public Parameters(BlockSize blockSize) {
+        public Parameters(BlockSize blockSize, boolean withContentChecksum) {
             this.blockSize = blockSize;
+            this.withContentChecksum = withContentChecksum;
         }
         @Override
         public String toString() {
-            return "LZ4 Parameters with BlockSize " + blockSize;
+            return "LZ4 Parameters with BlockSize " + blockSize + ", 
withContentChecksum " + withContentChecksum;
         }
     }
 
@@ -144,6 +147,7 @@ public class FramedLZ4CompressorOutputStream extends 
CompressorOutputStream {
 
     @Override
     public void write(byte[] data, int off, int len) throws IOException {
+        contentHash.update(data, off, len);
         if (currentIndex + len > blockData.length) {
             flushBlock();
             while (len > blockData.length) {
@@ -182,6 +186,9 @@ public class FramedLZ4CompressorOutputStream extends 
CompressorOutputStream {
     private void writeFrameDescriptor() throws IOException {
         int flags = FramedLZ4CompressorInputStream.SUPPORTED_VERSION
             | FramedLZ4CompressorInputStream.BLOCK_INDEPENDENCE_MASK;
+        if (params.withContentChecksum) {
+            flags |= FramedLZ4CompressorInputStream.CONTENT_CHECKSUM_MASK;
+        }
         out.write(flags);
         contentHash.update(flags);
         int bd = params.blockSize.getIndex() << 4;
@@ -211,7 +218,7 @@ public class FramedLZ4CompressorOutputStream extends 
CompressorOutputStream {
 
     private void writeTrailer() throws IOException {
         out.write(END_MARK);
-        // TODO content checksum
+        ByteUtils.toLittleEndian(out, contentHash.getValue(), 4);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/68fa6892/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStreamTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStreamTest.java
 
b/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStreamTest.java
index 0893fbe..3f5c701 100644
--- 
a/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStreamTest.java
+++ 
b/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorInputStreamTest.java
@@ -258,4 +258,26 @@ public final class FramedLZ4CompressorInputStreamTest
             assertThat(ex.getMessage(), containsString("content checksum"));
         }
     }
+
+    @Test
+    public void rejectsStreamsWithBadContentChecksum() throws IOException {
+        byte[] input = new byte[] {
+            4, 0x22, 0x4d, 0x18, // signature
+            0x64, // flag - Version 01, block independent, no block checksum, 
no content size, with content checksum
+            0x70, // block size 4MB
+            (byte) 185, // checksum
+            13, 0, 0, (byte) 0x80, // 13 bytes length and uncompressed bit set
+            'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', 
// content
+            0, 0, 0, 0, // empty block marker
+            1, 2, 3, 4,
+        };
+        try {
+            try (InputStream a = new FramedLZ4CompressorInputStream(new 
ByteArrayInputStream(input))) {
+                IOUtils.toByteArray(a);
+                fail("expected exception");
+            }
+        } catch (IOException ex) {
+            assertThat(ex.getMessage(), containsString("content checksum 
mismatch"));
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/68fa6892/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorRoundtripTest.java
----------------------------------------------------------------------
diff --git 
a/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorRoundtripTest.java
 
b/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorRoundtripTest.java
index 9744df7..a6aa5aa 100644
--- 
a/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorRoundtripTest.java
+++ 
b/src/test/java/org/apache/commons/compress/compressors/lz4/FramedLZ4CompressorRoundtripTest.java
@@ -39,10 +39,12 @@ public final class FramedLZ4CompressorRoundtripTest extends 
AbstractTestCase {
     @Parameters(name = "using {0}")
     public static Collection<Object[]> factory() {
         return Arrays.asList(new Object[][] {
-            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.K64)
 },
-            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.K256)
 },
-            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.M1)
 },
+            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.K64,
 true) },
+            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.K256,
 true) },
+            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.M1,
 true) },
             new Object[] { FramedLZ4CompressorOutputStream.Parameters.DEFAULT 
},
+            // default without content checksum
+            new Object[] { new 
FramedLZ4CompressorOutputStream.Parameters(FramedLZ4CompressorOutputStream.BlockSize.M4,
 false) },
         });
     }
 

Reply via email to