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();
+ }
+
}