Author: bodewig Date: Mon Jun 3 09:20:30 2013 New Revision: 1488935 URL: http://svn.apache.org/r1488935 Log: COMPRESS-229 properly parse GNU_LONGLNK entries, patch by Christoph Gysin
Added: commons/proper/compress/trunk/src/test/resources/longsymlink/ commons/proper/compress/trunk/src/test/resources/longsymlink/files.txt (with props) commons/proper/compress/trunk/src/test/resources/longsymlink/gnu.tar (with props) Modified: commons/proper/compress/trunk/src/changes/changes.xml commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/LongPathTest.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=1488935&r1=1488934&r2=1488935&view=diff ============================================================================== --- commons/proper/compress/trunk/src/changes/changes.xml (original) +++ commons/proper/compress/trunk/src/changes/changes.xml Mon Jun 3 09:20:30 2013 @@ -54,6 +54,12 @@ The <action> type attribute can be add,u to be able to read archives created by DotNetZip and maybe other archivers as well. </action> + <action type="fix" date="2013-06-03" issue="COMPRESS-229" + due-to="Christoph Gysin"> + TAR will now properly read the names of symbolic links with + long names that use the GNU variant to specify the long file + name. + </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/TarArchiveEntry.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java?rev=1488935&r1=1488934&r2=1488935&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java Mon Jun 3 09:20:30 2013 @@ -676,6 +676,16 @@ public class TarArchiveEntry implements } /** + * Indicate if this entry is a GNU long linkname block + * + * @return true if this is a long name extension provided by GNU tar + */ + public boolean isGNULongLinkEntry() { + return linkFlag == LF_GNUTYPE_LONGLINK + && name.equals(GNU_LONGLINK); + } + + /** * Indicate if this entry is a GNU long name block * * @return true if this is a long name extension provided by GNU tar Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java?rev=1488935&r1=1488934&r2=1488935&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java Mon Jun 3 09:20:30 2013 @@ -248,31 +248,13 @@ public class TarArchiveInputStream exten entryOffset = 0; entrySize = currEntry.getSize(); - if (currEntry.isGNULongNameEntry()) { - // read in the name - ByteArrayOutputStream longName = new ByteArrayOutputStream(); - int length = 0; - while ((length = read(SMALL_BUF)) >= 0) { - longName.write(SMALL_BUF, 0, length); - } - getNextEntry(); - if (currEntry == null) { - // Bugzilla: 40334 - // Malformed tar file - long entry name not followed by entry - return null; - } - byte[] longNameData = longName.toByteArray(); - // remove trailing null terminator(s) - length = longNameData.length; - while (length > 0 && longNameData[length - 1] == 0) { - --length; - } - if (length != longNameData.length) { - byte[] l = new byte[length]; - System.arraycopy(longNameData, 0, l, 0, length); - longNameData = l; - } + if (currEntry.isGNULongLinkEntry()) { + byte[] longLinkData = getLongNameData(); + currEntry.setLinkName(encoding.decode(longLinkData)); + } + if (currEntry.isGNULongNameEntry()) { + byte[] longNameData = getLongNameData(); currEntry.setName(encoding.decode(longNameData)); } @@ -293,6 +275,39 @@ public class TarArchiveInputStream exten } /** + * Get the next entry in this tar archive as longname data. + * + * @return The next entry in the archive as longname data, or null. + * @throws IOException on error + */ + protected byte[] getLongNameData() throws IOException { + // read in the name + ByteArrayOutputStream longName = new ByteArrayOutputStream(); + int length = 0; + while ((length = read(SMALL_BUF)) >= 0) { + longName.write(SMALL_BUF, 0, length); + } + getNextEntry(); + if (currEntry == null) { + // Bugzilla: 40334 + // Malformed tar file - long entry name not followed by entry + return null; + } + byte[] longNameData = longName.toByteArray(); + // remove trailing null terminator(s) + length = longNameData.length; + while (length > 0 && longNameData[length - 1] == 0) { + --length; + } + if (length != longNameData.length) { + byte[] l = new byte[length]; + System.arraycopy(longNameData, 0, l, 0, length); + longNameData = l; + } + return longNameData; + } + + /** * Get the next record in this tar archive. This will skip * over any remaining data in the current entry, if there * is one, and place the input stream at the header of the Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java?rev=1488935&r1=1488934&r2=1488935&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java Mon Jun 3 09:20:30 2013 @@ -233,6 +233,11 @@ public interface TarConstants { byte LF_CONTIG = (byte) '7'; /** + * Identifies the *next* file on the tape as having a long linkname. + */ + byte LF_GNUTYPE_LONGLINK = (byte) 'K'; + + /** * Identifies the *next* file on the tape as having a long name. */ byte LF_GNUTYPE_LONGNAME = (byte) 'L'; Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/LongPathTest.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/LongPathTest.java?rev=1488935&r1=1488934&r2=1488935&view=diff ============================================================================== --- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/LongPathTest.java (original) +++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/LongPathTest.java Mon Jun 3 09:20:30 2013 @@ -25,6 +25,8 @@ import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; import junit.framework.AssertionFailedError; import junit.framework.Test; @@ -33,6 +35,7 @@ import junit.framework.TestSuite; import org.apache.commons.compress.AbstractTestCase; import org.apache.commons.compress.archivers.ar.ArArchiveInputStream; import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; @@ -44,26 +47,37 @@ import org.apache.commons.compress.archi */ public class LongPathTest extends AbstractTestCase { + private String name; private File file; - private static final ArrayList<String> fileList = new ArrayList<String>(); + private static final Map<String, ArrayList<String>> fileLists = new HashMap<String, ArrayList<String>>(); public LongPathTest(String name) { super(name); } - private LongPathTest(String name, File file){ - super(name); + private LongPathTest(String name, String function, File file) { + super(function); + this.name = name; this.file = file; } public static TestSuite suite() throws IOException{ TestSuite suite = new TestSuite("LongPathTests"); - File arcdir = getFile("longpath"); + suite.addTest(createSuite("LongPathTest", "longpath")); + suite.addTest(createSuite("LongSymlinkTest", "longsymlink")); + return suite; + } + + public static TestSuite createSuite(String name, String dirname) throws IOException { + TestSuite suite = new TestSuite(name); + File arcdir = getFile(dirname); assertTrue(arcdir.exists()); File listing= new File(arcdir,"files.txt"); assertTrue("File listing is readable",listing.canRead()); BufferedReader br = new BufferedReader(new FileReader(listing)); + + ArrayList<String> fileList = new ArrayList<String>(); String line; while ((line=br.readLine())!=null){ if (line.startsWith("#")){ @@ -71,6 +85,7 @@ public class LongPathTest extends Abstra } fileList.add(line); } + fileLists.put(name, fileList); br.close(); File[]files=arcdir.listFiles(); for (final File file : files) { @@ -79,14 +94,25 @@ public class LongPathTest extends Abstra } // Appears to be the only way to give the test a variable name TestSuite namedSuite = new TestSuite(file.getName()); - Test test = new LongPathTest("testArchive", file); + Test test = new LongPathTest(name, "testArchive", file); namedSuite.addTest(test); suite.addTest(namedSuite); } return suite; } + protected String getExpectedString(ArchiveEntry entry) { + if (entry instanceof TarArchiveEntry) { + TarArchiveEntry tarEntry = (TarArchiveEntry) entry; + if (tarEntry.isSymbolicLink()) { + return tarEntry.getName() + " -> " + tarEntry.getLinkName(); + } + } + return entry.getName(); + } + public void testArchive() throws Exception { + ArrayList<String> fileList = fileLists.get(name); @SuppressWarnings("unchecked") // fileList is of correct type ArrayList<String> expected = (ArrayList<String>) fileList.clone(); String name = file.getName(); Added: commons/proper/compress/trunk/src/test/resources/longsymlink/files.txt URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/longsymlink/files.txt?rev=1488935&view=auto ============================================================================== --- commons/proper/compress/trunk/src/test/resources/longsymlink/files.txt (added) +++ commons/proper/compress/trunk/src/test/resources/longsymlink/files.txt Mon Jun 3 09:20:30 2013 @@ -0,0 +1 @@ +0xxxxxxxxx10xxxxxxxx20xxxxxxxx30xxxxxxxx40xxxxxxxx50xxxxxxxx60xxxxxxxx70xxxxxxxx80xxxxxxxx90xxxxxxxx100xxxxxxx110xxxxxxx120xxxxxxx130xxxxxxx -> 0yyyyyyyyy10yyyyyyyy20yyyyyyyy30yyyyyyyy40yyyyyyyy50yyyyyyyy60yyyyyyyy70yyyyyyyy80yyyyyyyy90yyyyyyyy100yyyyyyy110yyyyyyy120yyyyyyy130yyyyyyy Propchange: commons/proper/compress/trunk/src/test/resources/longsymlink/files.txt ------------------------------------------------------------------------------ svn:eol-style = native Added: commons/proper/compress/trunk/src/test/resources/longsymlink/gnu.tar URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/longsymlink/gnu.tar?rev=1488935&view=auto ============================================================================== Binary file - no diff available. Propchange: commons/proper/compress/trunk/src/test/resources/longsymlink/gnu.tar ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream