Return-Path: X-Original-To: apmail-commons-commits-archive@minotaur.apache.org Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 8E44382DC for ; Mon, 8 Aug 2011 08:55:29 +0000 (UTC) Received: (qmail 51204 invoked by uid 500); 8 Aug 2011 08:55:25 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 50743 invoked by uid 500); 8 Aug 2011 08:55:02 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 50682 invoked by uid 99); 8 Aug 2011 08:54:58 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 08 Aug 2011 08:54:58 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 08 Aug 2011 08:54:54 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 3CA2423889D5 for ; Mon, 8 Aug 2011 08:54:34 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1154877 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/archivers/ar/ site/xdoc/ test/java/org/apache/commons/compress/archivers/ar/ test/resources/ Date: Mon, 08 Aug 2011 08:54:34 -0000 To: commits@commons.apache.org From: bodewig@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110808085434.3CA2423889D5@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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? + * + *

From the FreeBSD ar(5) man page:

+ *
+     * 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.
+     * 
+ * + * @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

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.

+ 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.

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 l = new ArrayList(); - 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.