Author: bodewig Date: Fri Aug 9 06:51:18 2013 New Revision: 1512161 URL: http://svn.apache.org/r1512161 Log: COMPRESS-237 properly write TAR entries with long link names. Patch by Emmanuel Bourg
Modified: commons/proper/compress/trunk/src/changes/changes.xml commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java Modified: commons/proper/compress/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/changes/changes.xml?rev=1512161&r1=1512160&r2=1512161&view=diff ============================================================================== --- commons/proper/compress/trunk/src/changes/changes.xml (original) +++ commons/proper/compress/trunk/src/changes/changes.xml Fri Aug 9 06:51:18 2013 @@ -90,6 +90,11 @@ The <action> type attribute can be add,u CpioArchiveInputStream failed to read archives created by Redline RPM. </action> + <action type="fix" date="2013-08-09" issue="COMPRESS-237" + due-to="Emmanuel Bourg"> + TarArchiveOutputStream now properly handles link names that + are too long to fit into a traditional TAR header. + </action> </release> <release version="1.5" date="2013-03-14" description="Release 1.5"> 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=1512161&r1=1512160&r2=1512161&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 Aug 9 06:51:18 2013 @@ -271,31 +271,13 @@ public class TarArchiveOutputStream exte TarArchiveEntry entry = (TarArchiveEntry) archiveEntry; Map<String, String> paxHeaders = new HashMap<String, String>(); final String entryName = entry.getName(); - final ByteBuffer encodedName = encoding.encode(entryName); - final int nameLen = encodedName.limit() - encodedName.position(); - boolean paxHeaderContainsPath = false; - if (nameLen >= TarConstants.NAMELEN) { + boolean paxHeaderContainsPath = handleLongName(entryName, paxHeaders, "path", + TarConstants.LF_GNUTYPE_LONGNAME); - if (longFileMode == LONGFILE_POSIX) { - paxHeaders.put("path", entryName); - paxHeaderContainsPath = true; - } else if (longFileMode == LONGFILE_GNU) { - // create a TarEntry for the LongLink, the contents - // of which are the entry's name - TarArchiveEntry longLinkEntry = new TarArchiveEntry(TarConstants.GNU_LONGLINK, - TarConstants.LF_GNUTYPE_LONGNAME); - - longLinkEntry.setSize(nameLen + 1); // +1 for NUL - putArchiveEntry(longLinkEntry); - write(encodedName.array(), encodedName.arrayOffset(), nameLen); - write(0); // NUL terminator - closeArchiveEntry(); - } else if (longFileMode != LONGFILE_TRUNCATE) { - throw new RuntimeException("file name '" + entryName - + "' is too long ( > " - + TarConstants.NAMELEN + " bytes)"); - } - } + final String linkName = entry.getLinkName(); + boolean paxHeaderContainsLinkPath = linkName != null + && handleLongName(linkName, paxHeaders, "linkpath", + TarConstants.LF_GNUTYPE_LONGLINK); if (bigNumberMode == BIGNUMBER_POSIX) { addPaxHeadersForBigNumbers(paxHeaders, entry); @@ -308,10 +290,10 @@ public class TarArchiveOutputStream exte paxHeaders.put("path", entryName); } - if (addPaxHeadersForNonAsciiNames + if (addPaxHeadersForNonAsciiNames && !paxHeaderContainsLinkPath && (entry.isLink() || entry.isSymbolicLink()) - && !ASCII.canEncode(entry.getLinkName())) { - paxHeaders.put("linkpath", entry.getLinkName()); + && !ASCII.canEncode(linkName)) { + paxHeaders.put("linkpath", linkName); } if (paxHeaders.size() > 0) { @@ -630,4 +612,54 @@ public class TarArchiveOutputStream exte + maxValue + " )"); } } + + /** + * Handles long file or link names according to the longFileMode setting. + * + * <p>I.e. if the given name is too long to be written to a plain + * tar header then + * <ul> + * <li>it creates a pax header who's name is given by the + * paxHeaderName parameter if longFileMode is POSIX</li> + * <li>it creates a GNU longlink entry who's type is given by + * the linkType parameter if longFileMode is GNU</li> + * <li>throws an exception othewise.</li> + * </ul></p> + * + * @param name the name to write + * @param paxHeaders current map of pax headers + * @param paxHeaderName name of the pax header to write + * @param linkType type of the GNU entry to write + * @return whether a pax header has been written. + */ + private boolean handleLongName(String name, + Map<String, String> paxHeaders, + String paxHeaderName, byte linkType) + throws IOException { + final ByteBuffer encodedName = encoding.encode(name); + final int len = encodedName.limit() - encodedName.position(); + if (len >= TarConstants.NAMELEN) { + + if (longFileMode == LONGFILE_POSIX) { + paxHeaders.put(paxHeaderName, name); + return true; + } else if (longFileMode == LONGFILE_GNU) { + // create a TarEntry for the LongLink, the contents + // of which are the link's name + TarArchiveEntry longLinkEntry = + new TarArchiveEntry(TarConstants.GNU_LONGLINK, linkType); + + longLinkEntry.setSize(len + 1); // +1 for NUL + putArchiveEntry(longLinkEntry); + write(encodedName.array(), encodedName.arrayOffset(), len); + write(0); // NUL terminator + closeArchiveEntry(); + } else if (longFileMode != LONGFILE_TRUNCATE) { + throw new RuntimeException(paxHeaderName + " '" + name + + "' is too long ( > " + + TarConstants.NAMELEN + " bytes)"); + } + } + return false; + } }