Uploaded image for project: 'Commons Compress'
  1. Commons Compress
  2. COMPRESS-678

ArArchiveOutputStream doesn't pad correctly when a file name length is odd and greater than 16 (padding missing)

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Minor
    • Resolution: Fixed
    • 1.26.1
    • 1.26.2
    • Archivers
    • None

    Description

      Using ArArchiveInputStream, reading content created by ArArchiveOutputStream causes IOException "Invalid entry trailer." at specific conditions.

       

      Conditions

      1. LongFILE_BSD mode is enabled

      2. Ar file contains at least two entries.

          1. First entry's name length is longer than 16bytes and odd

          2. First entry's body length is odd.

          3. Second entry's name length is odd

       

      Cause

      ArArchiveOutputStream add padding if entryOffset is odd. This entryOffset only includes body length, but not entry name length.

      https://github.com/apache/commons-compress/blob/master/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveOutputStream.java#L80

       

      ArArchiveIutputStream try to remove padding when offset is odd. This offset includes body length and name length.

      https://github.com/apache/commons-compress/blob/master/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java#L266

       

      So encoding/decoding use different logics for padding, and at specific conditions, ArArchiveIutputStream remove 1byte that is actually not padding by mistake.

       

       

      Reproduction Code

       

      package test;
      
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
      import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
      import org.apache.commons.compress.archivers.ar.ArArchiveOutputStream;
      import org.junit.Test;
      
      public class ArTest {
      
        @Test
        public void test() throws IOException {
          File file = new File("test.ar");
          ArArchiveOutputStream arOut = new ArArchiveOutputStream(new FileOutputStream(file));
          arOut.setLongFileMode(ArArchiveOutputStream.LONGFILE_BSD);
          arOut.putArchiveEntry(new ArArchiveEntry("01234567891234567", 1));
          arOut.write(new byte[]{1});
          arOut.closeArchiveEntry();
      
          arOut.putArchiveEntry(new ArArchiveEntry("a", 1));
          arOut.write(new byte[]{1});
          arOut.closeArchiveEntry();
      
          arOut.close();
      
          ArArchiveInputStream arIn = new ArArchiveInputStream(new FileInputStream(file));
          ArArchiveEntry entry = arIn.getNextArEntry();
          System.out.println(entry.getName());
          arIn.readAllBytes();
      
          entry = arIn.getNextArEntry(); // <- This line causes the exception.     System.out.println(entry.getName());
        }
      }

       

       

       

      Error stack Trace

      java.io.IOException: Invalid entry trailer. not read the content? Occurred at byte: 146
      	at org.apache.commons.compress.archivers.ar.ArArchiveInputStream.getNextArEntry(ArArchiveInputStream.java:294)
      	at org.apache.druid.mila.ArTest.test(ArTest.java:34)
      	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
      	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
      	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
      	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
      	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
      	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
      	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
      	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
      	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
      	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
      	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
      	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
      	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
      	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
      	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
      	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
      	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
      	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
      	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
      	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
      	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
      	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:60)
      	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:52)
      	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
      	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
      	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
      	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
      	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
      	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
      	at jdk.proxy1/jdk.proxy1.$Proxy2.processTestClass(Unknown Source)
      	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
      	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
      	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
      	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
      	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
      	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
      	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
      	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
      	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74) 

      Attachments

        Activity

          People

            ggregory Gary D. Gregory
            tnakama888 takaaki nakama
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: