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.