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 2014/09/19 19:38:16 UTC

svn commit: r1626280 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java

Author: bodewig
Date: Fri Sep 19 17:38:15 2014
New Revision: 1626280

URL: http://svn.apache.org/r1626280
Log:
COMPRESS-289 use a stable last modified time for long name entry.

Modified:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java
    commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java?rev=1626280&r1=1626279&r2=1626280&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java Fri Sep 19 17:38:15 2014
@@ -271,12 +271,12 @@ public class TarArchiveOutputStream exte
         TarArchiveEntry entry = (TarArchiveEntry) archiveEntry;
         Map<String, String> paxHeaders = new HashMap<String, String>();
         final String entryName = entry.getName();
-        boolean paxHeaderContainsPath = handleLongName(entryName, paxHeaders, "path",
+        boolean paxHeaderContainsPath = handleLongName(entry, entryName, paxHeaders, "path",
                                                        TarConstants.LF_GNUTYPE_LONGNAME, "file name");
 
         final String linkName = entry.getLinkName();
         boolean paxHeaderContainsLinkPath = linkName != null && linkName.length() > 0
-            && handleLongName(linkName, paxHeaders, "linkpath",
+            && handleLongName(entry, linkName, paxHeaders, "linkpath",
                               TarConstants.LF_GNUTYPE_LONGLINK, "link name");
 
         if (bigNumberMode == BIGNUMBER_POSIX) {
@@ -637,6 +637,7 @@ public class TarArchiveOutputStream exte
      *   <li>it truncates the name if longFileMode is TRUNCATE</li>
      * </ul></p>
      *
+     * @param entry entry the name belongs to
      * @param name the name to write
      * @param paxHeaders current map of pax headers
      * @param paxHeaderName name of the pax header to write
@@ -644,7 +645,7 @@ public class TarArchiveOutputStream exte
      * @param fieldName the name of the field
      * @return whether a pax header has been written.
      */
-    private boolean handleLongName(String name,
+    private boolean handleLongName(TarArchiveEntry entry , String name,
                                    Map<String, String> paxHeaders,
                                    String paxHeaderName, byte linkType, String fieldName)
         throws IOException {
@@ -661,6 +662,7 @@ public class TarArchiveOutputStream exte
                 TarArchiveEntry longLinkEntry = new TarArchiveEntry(TarConstants.GNU_LONGLINK, linkType);
 
                 longLinkEntry.setSize(len + 1); // +1 for NUL
+                longLinkEntry.setModTime(entry.getModTime());
                 putArchiveEntry(longLinkEntry);
                 write(encodedName.array(), encodedName.arrayOffset(), len);
                 write(0); // NUL terminator

Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java?rev=1626280&r1=1626279&r2=1626280&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java (original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java Fri Sep 19 17:38:15 2014
@@ -23,17 +23,23 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.TimeZone;
 
 import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveOutputStream;
 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
 import org.apache.commons.compress.utils.CharsetNames;
 import org.apache.commons.compress.utils.IOUtils;
 
+import org.junit.Assert;
+
 public class TarArchiveOutputStreamTest extends AbstractTestCase {
 
     public void testCount() throws Exception {
@@ -386,7 +392,7 @@ public class TarArchiveOutputStreamTest 
             tos.putArchiveEntry(t);
             tos.closeArchiveEntry();
             tos.close();
-            
+
             fail("Truncated name didn't throw an exception");
         } catch (RuntimeException e) {
             // expected
@@ -498,7 +504,7 @@ public class TarArchiveOutputStreamTest 
                 + "01234567890123456789012345678901234567890123456789/test";
         TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
         entry.setLinkName(linkname);
-        
+
         try {
             ByteArrayOutputStream bos = new ByteArrayOutputStream();
             TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
@@ -506,7 +512,7 @@ public class TarArchiveOutputStreamTest 
             tos.putArchiveEntry(entry);
             tos.closeArchiveEntry();
             tos.close();
-            
+
             fail("Truncated link name didn't throw an exception");
         } catch (RuntimeException e) {
             // expected
@@ -519,14 +525,14 @@ public class TarArchiveOutputStreamTest 
             + "01234567890123456789012345678901234567890123456789/";
         TarArchiveEntry entry = new TarArchiveEntry("test" , TarConstants.LF_SYMLINK);
         entry.setLinkName(linkname);
-        
+
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
         tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE);
         tos.putArchiveEntry(entry);
         tos.closeArchiveEntry();
         tos.close();
-        
+
         byte[] data = bos.toByteArray();
         TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data));
         TarArchiveEntry e = tin.getNextTarEntry();
@@ -557,14 +563,14 @@ public class TarArchiveOutputStreamTest 
             + "01234567890123456789012345678901234567890123456789/test";
         TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
         entry.setLinkName(linkname);
-        
+
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
         tos.setLongFileMode(mode);
         tos.putArchiveEntry(entry);
         tos.closeArchiveEntry();
         tos.close();
-        
+
         byte[] data = bos.toByteArray();
         TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data));
         TarArchiveEntry e = tin.getNextTarEntry();
@@ -591,4 +597,57 @@ public class TarArchiveOutputStreamTest 
         assertEquals(TarConstants.DEFAULT_BLKSIZE, f.length());
     }
 
+    /**
+     * When using long file names the longLinkEntry included the
+     * current timestamp as the Entry modification date. This was
+     * never exposed to the client but it caused identical archives to
+     * have different MD5 hashes.
+     *
+     * @throws Exception
+     */
+    public void testLongNameMd5Hash() throws Exception {
+        final String longFileName = "a/considerably/longer/file/name/which/forces/use/of/the/long/link/header/which/appears/to/always/use/the/current/time/as/modification/date";
+        String fname = longFileName;
+        final Date modificationDate = new Date();
+
+        byte[] archive1 = createTarArchiveContainingOneDirectory(fname, modificationDate);
+        byte[] digest1 = MessageDigest.getInstance("MD5").digest(archive1);
+
+        // let a second elapse otherwise the modification dates will be equal
+        Thread.sleep(1000L);
+
+        // now recreate exactly the same tar file
+        byte[] archive2 = createTarArchiveContainingOneDirectory(fname, modificationDate);
+        // and I would expect the MD5 hash to be the same, but for long names it isn't
+        byte[] digest2 = MessageDigest.getInstance("MD5").digest(archive2);
+
+        Assert.assertArrayEquals(digest1, digest2);
+
+        // do I still have the correct modification date?
+        // let a second elapse so we don't get the current time
+        Thread.sleep(1000);
+        TarArchiveInputStream tarIn = new TarArchiveInputStream(new ByteArrayInputStream(archive2));
+        ArchiveEntry nextEntry = tarIn.getNextEntry();
+        assertEquals(longFileName, nextEntry.getName());
+        // tar archive stores modification time to second granularity only (floored)
+        assertEquals(modificationDate.getTime() / 1000, nextEntry.getLastModifiedDate().getTime() / 1000);
+        tarIn.close();
+    }
+
+    private static byte[] createTarArchiveContainingOneDirectory(String fname, Date modificationDate) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        TarArchiveOutputStream tarOut = new TarArchiveOutputStream(baos, 1024);
+        tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
+        TarArchiveEntry tarEntry = new TarArchiveEntry("d");
+        tarEntry.setModTime(modificationDate);
+        tarEntry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
+        tarEntry.setModTime(modificationDate.getTime());
+        tarEntry.setName(fname);
+        tarOut.putArchiveEntry(tarEntry);
+        tarOut.closeArchiveEntry();
+        tarOut.close();
+
+        return baos.toByteArray();
+    }
+
 }