[VFS-744] org.apache.commons.vfs2.FileContent.getByteArray() can throw
NegativeArraySizeException for BZip2 files
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileContent.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileContent.java
index 0162f3c..7e571f0 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileContent.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/FileContent.java
@@ -22,8 +22,10 @@
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.security.cert.Certificate;
+import java.util.Arrays;
import java.util.Map;
+import org.apache.commons.vfs2.provider.AbstractFileObject;
import org.apache.commons.vfs2.util.RandomAccessMode;
/**
@@ -92,15 +94,17 @@
if (sizeL > Integer.MAX_VALUE) {
throw new IllegalStateException(String.format("File content is too large for a byte array: %,d", sizeL));
}
- final int size = (int) sizeL;
+ final boolean sizeUndefined = sizeL < 0;
+ final int size = sizeUndefined ? AbstractFileObject.DEFAULT_BUFFER_SIZE : (int) sizeL;
final byte[] buf = new byte[size];
+ int pos;
try (final InputStream in = getInputStream(size)) {
int read = 0;
- for (int pos = 0; pos < size && read >= 0; pos += read) {
+ for (pos = 0; pos < size && read >= 0; pos += read) {
read = in.read(buf, pos, size - pos);
}
}
- return buf;
+ return sizeUndefined && pos < buf.length ? Arrays.copyOf(buf, ++pos) : buf;
}
/**
@@ -135,7 +139,7 @@
* @return An input stream to read the file's content from. The input stream is buffered, so there is no need to
* wrap it in a {@code BufferedInputStream}.
* @throws FileSystemException If the file does not exist, or is being read, or is being written, or on error
- * opening the stream.
+ * opening the stream.
*/
InputStream getInputStream() throws FileSystemException;
@@ -149,7 +153,7 @@
* @return An input stream to read the file's content from. The input stream is buffered, so there is no need to
* wrap it in a {@code BufferedInputStream}.
* @throws FileSystemException If the file does not exist, or is being read, or is being written, or on error
- * opening the stream.
+ * opening the stream.
* @since 2.4
*/
default InputStream getInputStream(final int bufferSize) throws FileSystemException {
@@ -161,7 +165,7 @@
*
* @return The last-modified timestamp.
* @throws FileSystemException If the file does not exist, or is being written to, or on error determining the
- * last-modified timestamp.
+ * last-modified timestamp.
*/
long getLastModifiedTime() throws FileSystemException;
@@ -178,7 +182,7 @@
* @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
* in a {@code BufferedOutputStream}.
* @throws FileSystemException If the file is read-only, or is being read, or is being written, or on error opening
- * the stream.
+ * the stream.
*/
OutputStream getOutputStream() throws FileSystemException;
@@ -196,7 +200,7 @@
* @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
* in a {@code BufferedOutputStream}.
* @throws FileSystemException If the file is read-only, or is being read, or is being written, or bAppend is true
- * and the implementation does not support it, or on error opening the stream.
+ * and the implementation does not support it, or on error opening the stream.
*/
OutputStream getOutputStream(boolean bAppend) throws FileSystemException;
@@ -215,7 +219,7 @@
* @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
* in a {@code BufferedOutputStream}.
* @throws FileSystemException If the file is read-only, or is being read, or is being written, or bAppend is true
- * and the implementation does not support it, or on error opening the stream.
+ * and the implementation does not support it, or on error opening the stream.
* @since 2.4
*/
default OutputStream getOutputStream(final boolean bAppend, final int bufferSize) throws FileSystemException {
@@ -236,7 +240,7 @@
* @return An output stream to write the file's content to. The stream is buffered, so there is no need to wrap it
* in a {@code BufferedOutputStream}.
* @throws FileSystemException If the file is read-only, or is being read, or is being written, or bAppend is true
- * and the implementation does not support it, or on error opening the stream.
+ * and the implementation does not support it, or on error opening the stream.
* @since 2.4
*/
default OutputStream getOutputStream(final int bufferSize) throws FileSystemException {
@@ -257,7 +261,7 @@
* @param mode The mode to use to access the file.
* @return the stream for reading and writing the file's content.
* @throws FileSystemException If the file is read-only, or is being read, or is being written, or on error opening
- * the stream.
+ * the stream.
*/
RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException;
@@ -325,7 +329,7 @@
*
* @param attrName The name of the attribute.
* @throws FileSystemException If the file does not exist, or is read-only, or does not support attributes, or on
- * error removing the attribute.
+ * error removing the attribute.
*/
void removeAttribute(String attrName) throws FileSystemException;
@@ -335,7 +339,7 @@
* @param attrName The name of the attribute.
* @param value The value of the attribute.
* @throws FileSystemException If the file does not exist, or is read-only, or does not support attributes, or on
- * error setting the attribute.
+ * error setting the attribute.
*/
void setAttribute(String attrName, Object value) throws FileSystemException;
@@ -344,7 +348,7 @@
*
* @param modTime The time to set the last-modified timestamp to.
* @throws FileSystemException If the file is read-only, or is being written to, or on error setting the
- * last-modified timestamp.
+ * last-modified timestamp.
*/
void setLastModifiedTime(long modTime) throws FileSystemException;
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
index f27c4de..04376b5 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileObject.java
@@ -70,7 +70,7 @@
/**
* Same as {@link BufferedInputStream}.
*/
- private static final int DEFAULT_BUFFER_SIZE = 8192;
+ public static final int DEFAULT_BUFFER_SIZE = 8192;
private static final FileName[] EMPTY_FILE_ARRAY = {};
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/compressed/CompressedFileFileObject.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/compressed/CompressedFileFileObject.java
index 3c03485..45de078 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/compressed/CompressedFileFileObject.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/compressed/CompressedFileFileObject.java
@@ -36,7 +36,7 @@
/**
* The value returned by {@link #doGetContentSize()} when not overriden by a subclass.
- *
+ *
* @since 2.5.0
*/
public static final int SIZE_UNDEFINED = -1;
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/bzip2/Bzip2TestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/bzip2/Bzip2TestCase.java
new file mode 100644
index 0000000..a6b10d8
--- /dev/null
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/bzip2/Bzip2TestCase.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.vfs2.provider.bzip2;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.AbstractVfsTestCase;
+import org.apache.commons.vfs2.FileContent;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.VFS;
+import org.apache.commons.vfs2.provider.compressed.CompressedFileFileObject;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Bzip2TestCase extends AbstractVfsTestCase {
+
+ @Test
+ public void testBZip2() throws IOException {
+ final File testResource = getTestResource("bla.txt.bz2");
+ try (final FileObject bz2FileObject = VFS.getManager().resolveFile("bz2://" + testResource)) {
+ Assert.assertTrue(bz2FileObject.exists());
+ Assert.assertTrue(bz2FileObject.isFolder());
+ try (final FileObject fileObjectDir = bz2FileObject.resolveFile("bla.txt")) {
+ Assert.assertTrue(fileObjectDir.exists());
+ Assert.assertTrue(bz2FileObject.isFolder());
+ try (final FileObject fileObject = fileObjectDir.resolveFile("bla.txt")) {
+ Assert.assertTrue(fileObject.exists());
+ Assert.assertFalse(fileObject.isFolder());
+ Assert.assertTrue(fileObject.isFile());
+ try (final FileContent content = fileObject.getContent()) {
+ Assert.assertEquals(CompressedFileFileObject.SIZE_UNDEFINED, content.getSize());
+ // blows up, Commons Compress?
+ final String string = content.getString(StandardCharsets.UTF_8);
+ Assert.assertEquals(26, string.length());
+ Assert.assertEquals("Hallo, dies ist ein Test.\n", string);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/commons-vfs2/src/test/resources/test-data/bla.txt.bz2 b/commons-vfs2/src/test/resources/test-data/bla.txt.bz2
new file mode 100644
index 0000000..87309da
--- /dev/null
+++ b/commons-vfs2/src/test/resources/test-data/bla.txt.bz2
Binary files differ
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 9fc9f3f..00f1ff6 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -110,6 +110,9 @@
<action issue="VFS-743" dev="ggregory" type="add" due-to="Gary Gregory">
Add org.apache.commons.vfs2.provider.compressed.CompressedFileFileObject.SIZE_UNDEFINED.
</action>
+ <action issue="VFS-744" dev="ggregory" type="fix" due-to="Gary Gregory">
+ org.apache.commons.vfs2.FileContent.getByteArray() can throw NegativeArraySizeException for BZip2 files.
+ </action>
</release>
<release version="2.4.1" date="2019-08-10" description="Bug fix release.">
<action issue="VFS-725" dev="ggregory" type="fix" due-to="Gary Gregory">