Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ The <action> type attribute can be add,update,fix,remove.
<action type="fix" dev="pkarwasz" due-to="Tyler Nighswander">Align DUMP archive block size with Linux `dump` utility.</action>
<action type="fix" dev="ggregory" due-to="Tyler Nighswander, Gary Gregory">DumpArchiveInputStream.getNextEntry() throws an ArchiveException instead of ArrayIndexOutOfBoundsException.</action>
<action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz">Fix DumpArchiveInputStream to correctly handle file names up to 255 bytes #711.</action>
<!-- FIX zip -->
<!-- FIX zip -->
<action type="fix" dev="ggregory" due-to="Dominik Stadler, Gary Gregory" issue="COMPRESS-598">ZipArchiveInputStream.read(byte[], int, int) now throws an IOException instead of a NullPointerException.</action>
<action type="fix" dev="ggregory" due-to="Tyler Nighswander, Gary Gregory">ZipFile.createBoundedInputStream(long, long) now throws an ArchiveException instead of IllegalArgumentException.</action>
<action type="fix" dev="ggregory" due-to="Tyler Nighswander, Gary Gregory">ZipFile.getContentBeforeFirstLocalFileHeader() now throws an ArchiveException instead of IllegalArgumentException.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">ExplodingInputStream now throws ArchiveException instead of ArithmeticException.</action>
<action type="fix" dev="ggregory" due-to="Tyler Nighswander, Gary Gregory">ZipArchiveInputStream.read() now throws an IOException instead of java.lang.ArrayIndexOutOfBoundsException.</action>
<action type="fix" dev="ggregory" due-to="Stanislav Fort, Gary Gregory">ZipArchiveInputStream now throws an MemoryLimitException instead of ArchiveException, or OutOfMemoryError when running with low memory settings set on the command line.</action>
<action type="fix" dev="ggregory" due-to="YuWeiBoy, Gary Gregory" issue="COMPRESS-708">ZstdCompressorInputStream closes the InputStream held by ZipArchiveInputStream garbage collection.</action>
<!-- FIX tar -->
<!-- FIX tar -->
<action type="fix" dev="pkarwasz" due-to="Tyler Nighswander, Piotr P. Karwasz, Gary Gregory">>Uniform handling of special tar records in TarFile and TarArchiveInputStream.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory, Stanislav Fort">TarArchiveOutputStream now throws a IllegalArgumentException instead of an OutOfMemoryError.</action>
<action type="fix" dev="ggregory" issue="COMPRESS-707" due-to="Gary Gregory, Roel van Dijk">TarUtils.verifyCheckSum() throws an Exception when checksum could not be parsed.</action>
Expand All @@ -84,7 +84,7 @@ The <action> type attribute can be add,update,fix,remove.
<!-- FIX pack200 -->
<action type="fix" dev="ggregory" due-to="Gary Gregory, Igor Morgenstern">org.apache.commons.compress.harmony.pack200 now throws Pack200Exception, IllegalArgumentException, IllegalStateException, instead of other runtime exceptions and Error.</action>
<action type="fix" dev="ppkarwasz" due-to="Raeps">Extract duplicate code in org.apache.commons.compress.harmony.pack200.IntList.</action>
<!-- FIX cpio -->
<!-- FIX cpio -->
<action type="fix" dev="ggregory" due-to="Stan, Gary Gregory">CpioArchiveEntry now throws ArchiveException instead of Arithmetic exception.</action>
<action type="fix" dev="ggregory" due-to="Stan, Gary Gregory">CpioArchiveInputStream.getNextEntry() now throws a MemoryLimitException instead of OutOfMemoryError when it can't process input greater than available memory.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">CpioArchiveInputStream.readOldAsciiEntry(boolean) now throws ArchiveException instead of Arithmetic exception.</action>
Expand Down Expand Up @@ -126,6 +126,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary Gregory">TarFile now implements IOIterable&lt;TarArchiveEntry&gt;.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add a builder for the TarFile class and deprecate some constructors.</action>
<action type="add" dev="pkarwasz" due-to="Piotr Karwasz">SevenZFile, TarFile, and ZipFile now always close underlying resources when builder or constructor fails.</action>
<action type="add" dev="pkarwasz" due-to="Piotr P. Karwasz">Introduce an ArchiveFile abstraction to unify the APIs of SevenZFile, TarFile, and ZipFile.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 85 to 88 #707.</action>
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0.</action>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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
*
* https://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.compress.archivers;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipFile;

import org.apache.commons.io.function.IOIterable;
import org.apache.commons.io.function.IOIterator;
import org.apache.commons.io.function.IOStream;

/**
* A file-based representation of an archive containing multiple {@link ArchiveEntry entries}.
*
* <p>This interface provides a higher-level abstraction over archive files, similar to
* {@link ZipFile}, but generalized for a variety of archive formats.</p>
*
* <p>Implementations are {@link Closeable} and should be closed once they are no longer
* needed in order to release any underlying system resources.</p>
*
* @param <T> the type of {@link ArchiveEntry} produced by this archive
* @since 1.29.0
*/
public interface ArchiveFile<T extends ArchiveEntry> extends Closeable, IOIterable<T> {

/**
* Returns all entries contained in the archive as a list.
*
* <p>The order of entries is format-dependent but guaranteed to be consistent
* across multiple invocations on the same archive.</p>
*
* @return An immutable list of all entries in this archive.
*/
default List<? extends T> entries() {
try (IOStream<? extends T> stream = stream()) {
return stream.collect(Collectors.toList());
}
}

/**
* Returns a sequential stream of archive entries.
*
* <p>The order of entries is format-dependent but stable for a given archive.</p>
* <p>The returned stream <strong>must</strong> be closed after use to free
* associated resources.</p>
*
* @return A stream of entries in this archive.
*/
IOStream<? extends T> stream();

/**
* Opens an input stream for the specified entry's contents.
*
* <p>The caller is responsible for closing the returned stream after use.</p>
*
* @param entry The archive entry to read.
* @return An input stream providing the contents of the given entry.
* @throws IOException If an I/O error occurs while opening the entry stream.
*/
InputStream getInputStream(T entry) throws IOException;

@Override
@SuppressWarnings("unchecked")
default IOIterator<T> iterator() {
return IOIterator.adapt((Iterable<T>) entries());
}

@Override
default Iterable<T> unwrap() {
return asIterable();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
Expand All @@ -48,8 +47,10 @@
import org.apache.commons.compress.MemoryLimitException;
import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveFile;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.InputStreamStatistics;
import org.apache.commons.io.function.IOStream;
import org.apache.commons.io.input.BoundedInputStream;
import org.apache.commons.io.input.ChecksumInputStream;
import org.apache.commons.lang3.ArrayUtils;
Expand Down Expand Up @@ -77,7 +78,7 @@
* @NotThreadSafe
* @since 1.6
*/
public class SevenZFile implements Closeable {
public class SevenZFile implements ArchiveFile<SevenZArchiveEntry> {

private static final class ArchiveStatistics {
private int numberOfPackedStreams;
Expand Down Expand Up @@ -1029,11 +1030,22 @@ public String getDefaultName() {
*
* @return a copy of meta-data of all archive entries.
* @since 1.11
* @deprecated Since 1.29.0, use {@link #entries()} or {@link #stream()} instead.
*/
@Deprecated
public Iterable<SevenZArchiveEntry> getEntries() {
return new ArrayList<>(Arrays.asList(archive.files));
}

/**
* {@inheritDoc}
* @since 1.29.0
*/
@Override
public IOStream<? extends SevenZArchiveEntry> stream() {
return IOStream.of(archive.files);
}

/**
* Gets an InputStream for reading the contents of the given entry.
* <p>
Expand All @@ -1045,6 +1057,7 @@ public Iterable<SevenZArchiveEntry> getEntries() {
* @throws IOException if unable to create an input stream from the entry
* @since 1.20
*/
@Override
public InputStream getInputStream(final SevenZArchiveEntry entry) throws IOException {
int entryIndex = -1;
for (int i = 0; i < archive.files.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/
package org.apache.commons.compress.archivers.tar;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -28,28 +27,27 @@
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveFile;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.BoundedArchiveInputStream;
import org.apache.commons.compress.utils.BoundedSeekableByteChannelInputStream;
import org.apache.commons.io.function.IOIterable;
import org.apache.commons.io.function.IOIterator;
import org.apache.commons.io.function.IOStream;
import org.apache.commons.io.input.BoundedInputStream;

/**
* Provides random access to Unix archives.
*
* @since 1.21
*/
public class TarFile implements Closeable, IOIterable<TarArchiveEntry> {
public class TarFile implements ArchiveFile<TarArchiveEntry> {

private final class BoundedTarEntryInputStream extends BoundedArchiveInputStream {

Expand Down Expand Up @@ -226,7 +224,13 @@ private TarFile(final Builder builder) throws IOException {
this.recordBuffer = ByteBuffer.allocate(this.recordSize);
this.blockSize = builder.getBlockSize();
this.lenient = builder.isLenient();
forEach(entries::add);
// Populate `entries` explicitly here instead of using `forEach`/`stream`,
// because both rely on `entries` internally.
// Using them would cause a self-referential loop and leave `entries` empty.
TarArchiveEntry entry;
while ((entry = getNextTarEntry()) != null) {
entries.add(entry);
}
} catch (IOException ex) {
try {
this.archive.close();
Expand Down Expand Up @@ -442,18 +446,30 @@ private void consumeRemainderOfLastBlock() throws IOException {
* Gets all TAR Archive Entries from the TarFile.
*
* @return All entries from the tar file.
* @deprecated Since 1.29.0, use {@link #entries()} or {@link #stream()} instead.
*/
@Deprecated
public List<TarArchiveEntry> getEntries() {
return new ArrayList<>(entries);
}

/**
* {@inheritDoc}
* @since 1.29.0
*/
@Override
public IOStream<? extends TarArchiveEntry> stream() {
return IOStream.of(entries);
}

/**
* Gets the input stream for the provided Tar Archive Entry.
*
* @param entry Entry to get the input stream from.
* @return Input stream of the provided entry.
* @throws IOException Corrupted TAR archive. Can't read entry.
*/
@Override
public InputStream getInputStream(final TarArchiveEntry entry) throws IOException {
try {
return new BoundedTarEntryInputStream(entry, archive);
Expand Down Expand Up @@ -567,38 +583,6 @@ private boolean isEOFRecord(final ByteBuffer headerBuf) {
return headerBuf == null || ArchiveUtils.isArrayZero(headerBuf.array(), recordSize);
}

@Override
public IOIterator<TarArchiveEntry> iterator() {
return new IOIterator<TarArchiveEntry>() {

private TarArchiveEntry next;

@Override
public boolean hasNext() throws IOException {
if (next == null) {
next = getNextTarEntry();
}
return next != null;
}

@Override
public TarArchiveEntry next() throws IOException {
if (next == null) {
next = getNextTarEntry();
}
final TarArchiveEntry tmp = next;
next = null;
return tmp;
}

@Override
public Iterator<TarArchiveEntry> unwrap() {
return null;
}

};
}

/**
* Adds the sparse chunks from the current entry to the sparse chunks, including any additional sparse entries following the current entry.
*
Expand Down Expand Up @@ -704,12 +688,4 @@ private void tryToConsumeSecondEOFRecord() throws IOException {
}
}
}

@Override
public Iterable<TarArchiveEntry> unwrap() {
// Commons IO 2.21.0:
// return asIterable();
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -52,6 +51,7 @@

import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveFile;
import org.apache.commons.compress.archivers.EntryStreamOffsets;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
Expand Down Expand Up @@ -88,7 +88,7 @@
* <li>close is allowed to throw IOException.</li>
* </ul>
*/
public class ZipFile implements Closeable {
public class ZipFile implements ArchiveFile<ZipArchiveEntry> {

/**
* Lock-free implementation of BoundedInputStream. The implementation uses positioned reads on the underlying archive file channel and therefore performs
Expand Down Expand Up @@ -1135,7 +1135,9 @@ public String getEncoding() {
* </p>
*
* @return all entries as {@link ZipArchiveEntry} instances
* @deprecated Since 1.29.0, use {@link #entries()} or {@link #stream()} instead.
*/
@Deprecated
public Enumeration<ZipArchiveEntry> getEntries() {
return Collections.enumeration(entries);
}
Expand Down Expand Up @@ -1208,6 +1210,7 @@ public long getFirstLocalFileHeaderOffset() {
* @return a stream to read the entry from. The returned stream implements {@link InputStreamStatistics}.
* @throws IOException if unable to create an input stream from the zipEntry.
*/
@Override
public InputStream getInputStream(final ZipArchiveEntry entry) throws IOException {
if (!(entry instanceof Entry)) {
return null;
Expand Down Expand Up @@ -1738,6 +1741,7 @@ private boolean startsWithLocalFileHeader() throws IOException {
* @throws IllegalStateException if the ZIP file has been closed.
* @since 1.28.0
*/
@Override
public IOStream<? extends ZipArchiveEntry> stream() {
return IOStream.adapt(entries.stream());
}
Expand Down
Loading