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


Reply via email to