diff --git a/WHATSNEW b/WHATSNEW index 0f052c90b..43fb6a5b7 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -32,6 +32,11 @@ Fixed bugs: a NullPointerException when using FTP. Bugzilla Report 56873 + * Long-Name and -link or PAX-header entries in TAR archives + always had the current time as last modfication time, creating + archives that are different at the byte level each time an + archive was built. + Other changes: -------------- diff --git a/src/main/org/apache/tools/tar/TarOutputStream.java b/src/main/org/apache/tools/tar/TarOutputStream.java index 03c8769e5..81626340f 100644 --- a/src/main/org/apache/tools/tar/TarOutputStream.java +++ b/src/main/org/apache/tools/tar/TarOutputStream.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; import java.nio.ByteBuffer; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -273,12 +274,12 @@ public class TarOutputStream extends FilterOutputStream { } Map paxHeaders = new HashMap(); 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 - && handleLongName(linkName, paxHeaders, "linkpath", + boolean paxHeaderContainsLinkPath = linkName != null && linkName.length() > 0 + && handleLongName(entry, linkName, paxHeaders, "linkpath", TarConstants.LF_GNUTYPE_LONGLINK, "link name"); if (bigNumberMode == BIGNUMBER_POSIX) { @@ -299,7 +300,7 @@ public class TarOutputStream extends FilterOutputStream { } if (paxHeaders.size() > 0) { - writePaxHeaders(entryName, paxHeaders); + writePaxHeaders(entry, entryName, paxHeaders); } entry.writeEntryHeader(recordBuf, encoding, @@ -465,7 +466,8 @@ public class TarOutputStream extends FilterOutputStream { /** * Writes a PAX extended header with the given map as contents. */ - void writePaxHeaders(String entryName, + void writePaxHeaders(TarEntry entry, + String entryName, Map headers) throws IOException { String name = "./PaxHeaders.X/" + stripTo7Bits(entryName); if (name.length() >= TarConstants.NAMELEN) { @@ -478,6 +480,7 @@ public class TarOutputStream extends FilterOutputStream { } TarEntry pex = new TarEntry(name, TarConstants.LF_PAX_EXTENDED_HEADER_LC); + transferModTime(entry, pex); StringWriter w = new StringWriter(); for (Map.Entry h : headers.entrySet()) { @@ -537,7 +540,8 @@ public class TarOutputStream extends FilterOutputStream { TarConstants.MAXSIZE); addPaxHeaderForBigNumber(paxHeaders, "gid", entry.getGroupId(), TarConstants.MAXID); - addPaxHeaderForBigNumber(paxHeaders, "mtime", entry.getModTime().getTime() / 1000, + addPaxHeaderForBigNumber(paxHeaders, "mtime", + entry.getModTime().getTime() / 1000, TarConstants.MAXSIZE); addPaxHeaderForBigNumber(paxHeaders, "uid", entry.getUserId(), TarConstants.MAXID); @@ -594,6 +598,7 @@ public class TarOutputStream extends FilterOutputStream { *
  • it truncates the name if longFileMode is TRUNCATE
  • *

    * + * @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 @@ -601,7 +606,7 @@ public class TarOutputStream extends FilterOutputStream { * @param fieldName the name of the field * @return whether a pax header has been written. */ - private boolean handleLongName(String name, + private boolean handleLongName(TarEntry entry , String name, Map paxHeaders, String paxHeaderName, byte linkType, String fieldName) throws IOException { @@ -619,6 +624,7 @@ public class TarOutputStream extends FilterOutputStream { new TarEntry(TarConstants.GNU_LONGLINK, linkType); longLinkEntry.setSize(len + 1); // +1 for NUL + transferModTime(entry, longLinkEntry); putNextEntry(longLinkEntry); write(encodedName.array(), encodedName.arrayOffset(), len); write(0); // NUL terminator @@ -631,4 +637,13 @@ public class TarOutputStream extends FilterOutputStream { } return false; } + + private void transferModTime(TarEntry from, TarEntry to) { + Date fromModTime = from.getModTime(); + long fromModTimeSeconds = fromModTime.getTime() / 1000; + if (fromModTimeSeconds < 0 || fromModTimeSeconds > TarConstants.MAXSIZE) { + fromModTime = new Date(0); + } + to.setModTime(fromModTime); + } }