Author: bodewig Date: Mon Aug 8 08:54:33 2011 New Revision: 1154877 URL: http://svn.apache.org/viewvc?rev=1154877&view=rev Log: support reading of long file names in AR archives that use the BSD variant. COMPRESS-144
Added: commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar - copied, changed from r1154851, commons/proper/compress/trunk/src/test/resources/longfile_gnu.ar Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java commons/proper/compress/trunk/src/site/xdoc/examples.xml commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java?rev=1154877&r1=1154876&r2=1154877&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java Mon Aug 8 08:54:33 2011 @@ -18,6 +18,7 @@ */ package org.apache.commons.compress.archivers.ar; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -143,6 +144,7 @@ public class ArArchiveInputStream extend // entry name is stored as ASCII string String temp = ArchiveUtils.toAsciiString(name).trim(); + long len = asLong(length); if (temp.equals("//")){ // GNU extended filenames entry int bufflen = asInt(length); // Assume length will fit in an int @@ -158,8 +160,17 @@ public class ArArchiveInputStream extend } else if (temp.matches("^/\\d+")) {// GNU long filename ref. int offset = Integer.parseInt(temp.substring(1));// get the offset temp = getExtendedName(offset); // convert to the long name + } else if (isBSDLongName(temp)) { + temp = getBSDLongName(temp); + // entry length contained the length of the file name in + // addition to the real length of the entry. + // assume file name was ASCII, there is no "standard" otherwise + int nameLen = temp.length(); + len -= nameLen; + entryOffset += nameLen; } - currentEntry = new ArArchiveEntry(temp, asLong(length), asInt(userid, true), + + currentEntry = new ArArchiveEntry(temp, len, asInt(userid, true), asInt(groupid, true), asInt(filemode, 8), asLong(lastmodified)); return currentEntry; @@ -301,4 +312,58 @@ public class ArArchiveInputStream extend return true; } + private static final String BSD_LONGNAME_PREFIX = "#1/"; + private static final int BSD_LONGNAME_PREFIX_LEN = + BSD_LONGNAME_PREFIX.length(); + + /** + * Does the name look like it is a long name (or a name containing + * spaces) as encoded by BSD ar? + * + * <p>From the FreeBSD ar(5) man page:</p> + * <pre> + * BSD In the BSD variant, names that are shorter than 16 + * characters and without embedded spaces are stored + * directly in this field. If a name has an embedded + * space, or if it is longer than 16 characters, then + * the string "#1/" followed by the decimal represen- + * tation of the length of the file name is placed in + * this field. The actual file name is stored immedi- + * ately after the archive header. The content of the + * archive member follows the file name. The ar_size + * field of the header (see below) will then hold the + * sum of the size of the file name and the size of + * the member. + * </pre> + * + * @since Apache Commons Compress 1.3 + */ + private static boolean isBSDLongName(String name) { + return name.startsWith(BSD_LONGNAME_PREFIX) + && name.length() > BSD_LONGNAME_PREFIX_LEN; + } + + /** + * Reads the real name from the current stream assuming the very + * first bytes to be read are the real file name. + * + * @see #isBSDLongName + */ + private String getBSDLongName(String bsdLongName) throws IOException { + int nameLen = + Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN)); + byte[] name = new byte[nameLen]; + int read = 0, readNow = 0; + while ((readNow = input.read(name, read, nameLen - read)) >= 0) { + read += readNow; + count(readNow); + if (read == nameLen) { + break; + } + } + if (read != nameLen) { + throw new EOFException(); + } + return ArchiveUtils.toAsciiString(name); + } } Modified: commons/proper/compress/trunk/src/site/xdoc/examples.xml URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/site/xdoc/examples.xml?rev=1154877&r1=1154876&r2=1154877&view=diff ============================================================================== --- commons/proper/compress/trunk/src/site/xdoc/examples.xml (original) +++ commons/proper/compress/trunk/src/site/xdoc/examples.xml Mon Aug 8 08:54:33 2011 @@ -98,10 +98,11 @@ LOOP UNTIL entry.getSize() HAS BEEN READ <p>Traditionally the AR format doesn't allow file names longer than 16 characters. There are two variants that circumvent this limitation in different ways, the GNU/SRV4 and the BSD - variant. Currently Commons Compress can only read archives - using the GNU/SRV4 variant, it doesn't support writing - archives with file names longer than 16 characters at - all.</p> + variant. Commons Compress 1.0 to 1.2 can only read archives + using the GNU/SRV4 variant, support for the BSD variant has + been added in Commons Compress 1.3. It doesn't support + writing archives with file names longer than 16 characters + at all.</p> </subsection> Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java?rev=1154877&r1=1154876&r2=1154877&view=diff ============================================================================== --- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java (original) +++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java Mon Aug 8 08:54:33 2011 @@ -18,15 +18,39 @@ package org.apache.commons.compress.archivers.ar; -import java.util.ArrayList; +import java.io.BufferedInputStream; +import java.io.FileInputStream; import org.apache.commons.compress.AbstractTestCase; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.utils.ArchiveUtils; public class ArArchiveInputStreamTest extends AbstractTestCase { public void testReadLongNamesGNU() throws Exception { - ArrayList<String> l = new ArrayList<String>(); - l.add("this_is_a_long_file_name.txt"); - checkArchiveContent(getFile("longfile_gnu.ar"), l); + checkLongNameEntry("longfile_gnu.ar"); } + public void testReadLongNamesBSD() throws Exception { + checkLongNameEntry("longfile_bsd.ar"); + } + + private void checkLongNameEntry(String archive) throws Exception { + FileInputStream fis = new FileInputStream(getFile(archive)); + ArArchiveInputStream s = null; + try { + s = new ArArchiveInputStream(new BufferedInputStream(fis)); + ArchiveEntry e = s.getNextEntry(); + assertEquals("this_is_a_long_file_name.txt", e.getName()); + assertEquals(14, e.getSize()); + byte[] hello = new byte[14]; + s.read(hello); + assertEquals("Hello, world!\n", ArchiveUtils.toAsciiString(hello)); + assertNull(s.getNextEntry()); + } finally { + if (s != null) { + s.close(); + } + fis.close(); + } + } } Copied: commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar (from r1154851, commons/proper/compress/trunk/src/test/resources/longfile_gnu.ar) URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar?p2=commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar&p1=commons/proper/compress/trunk/src/test/resources/longfile_gnu.ar&r1=1154851&r2=1154877&rev=1154877&view=diff ============================================================================== Binary files - no diff available.