You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by pk...@apache.org on 2025/09/25 07:20:42 UTC

(commons-compress) branch feat/max-file-name-length created (now 926b7dc52)

This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a change to branch feat/max-file-name-length
in repository https://gitbox.apache.org/repos/asf/commons-compress.git


      at 926b7dc52 feat: Add configurable maximum entry name length

This branch includes the following new commits:

     new 926b7dc52 feat: Add configurable maximum entry name length

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(commons-compress) 01/01: feat: Add configurable maximum entry name length

Posted by pk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch feat/max-file-name-length
in repository https://gitbox.apache.org/repos/asf/commons-compress.git

commit 926b7dc527c4520c64cb78eff03a90cde9301d5e
Author: Piotr P. Karwasz <pk...@apache.org>
AuthorDate: Wed Sep 24 21:43:38 2025 +0200

    feat: Add configurable maximum entry name length
    
    This change introduces a configurable limit on archive entry names.
    
    Although formats like **AR**, **CPIO**, and **TAR** permit arbitrarily long path names, real operating systems and file systems impose much stricter limits:
    
    * Individual path segments (file names) are typically limited to 255 bytes.
    * Full paths are usually capped at a few KiB (e.g. 1024 bytes on macOS via `MAX_PATH`).
    
    #### What’s new
    
    * Added a new common builder, `AbstractArchiveBuilder`, inserted in the hierarchy between archive stream builders and `AbstractStreamBuilder`.
    * Introduced a new configuration option: `setMaxEntryLength`.
    * Default value is `Short.MAX_VALUE`, which is higher than any realistic OS limit.
    * Enforced the limit across:
    
      * All `ArchiveInputStream` implementations
      * `SevenZFile`, `TarFile`, and `ZipFile`
    * Added a dedicated test suite to verify:
    
      * Entry names up to `Short.MAX_VALUE` are handled correctly.
      * Entries exceeding a lowered limit result in an exception.
---
 src/changes/changes.xml                            |   1 +
 .../compress/archivers/AbstractArchiveBuilder.java |  77 +++++
 .../compress/archivers/ArchiveInputStream.java     |  72 +++--
 .../archivers/ar/ArArchiveInputStream.java         |  17 +-
 .../archivers/arj/ArjArchiveInputStream.java       |  28 +-
 .../archivers/cpio/CpioArchiveInputStream.java     |  16 +-
 .../archivers/dump/DumpArchiveInputStream.java     |  18 +-
 .../compress/archivers/sevenz/SevenZFile.java      |  29 +-
 .../compress/archivers/tar/AbstractTarBuilder.java |   4 +-
 .../archivers/tar/TarArchiveInputStream.java       |  15 +-
 .../commons/compress/archivers/tar/TarFile.java    |  17 +-
 .../commons/compress/archivers/tar/TarUtils.java   | 118 ++++---
 .../archivers/zip/ZipArchiveInputStream.java       |   8 +-
 .../commons/compress/archivers/zip/ZipFile.java    |  81 ++++-
 .../commons/compress/utils/ArchiveUtils.java       |  27 ++
 .../compress/archivers/MaxNameEntryLengthTest.java | 348 +++++++++++++++++++++
 .../archivers/cpio/CpioArchiveInputStreamTest.java |  28 --
 .../archivers/tar/TarArchiveInputStreamTest.java   |   8 +-
 .../compress/archivers/tar/TarFileTest.java        |   3 +-
 .../compress/archivers/tar/TarUtilsTest.java       |  24 +-
 .../synthetic/long-name/bin-big-endian.cpio        | Bin 0 -> 32794 bytes
 .../synthetic/long-name/bin-little-endian.cpio     | Bin 0 -> 32794 bytes
 src/test/resources/synthetic/long-name/bsd-fail.ar | Bin 0 -> 68 bytes
 .../synthetic/long-name/bsd-short-max-value.ar     | Bin 0 -> 32836 bytes
 .../resources/synthetic/long-name/crc-fail.cpio    | Bin 0 -> 113 bytes
 src/test/resources/synthetic/long-name/crc.cpio    |   1 +
 src/test/resources/synthetic/long-name/gnu-fail.ar | Bin 0 -> 68 bytes
 .../resources/synthetic/long-name/gnu-fail.tar     | Bin 0 -> 512 bytes
 .../synthetic/long-name/gnu-short-max-value.ar     | Bin 0 -> 32896 bytes
 src/test/resources/synthetic/long-name/gnu.tar     | Bin 0 -> 34816 bytes
 .../synthetic/long-name/long-name-reversed.dump    | Bin 0 -> 267264 bytes
 .../resources/synthetic/long-name/long-name.7z     | Bin 0 -> 65587 bytes
 .../resources/synthetic/long-name/long-name.arj    | Bin 0 -> 2669 bytes
 .../resources/synthetic/long-name/long-name.dump   | Bin 0 -> 267264 bytes
 .../resources/synthetic/long-name/long-name.zip    | Bin 0 -> 65632 bytes
 .../resources/synthetic/long-name/newc-fail.cpio   | Bin 0 -> 113 bytes
 src/test/resources/synthetic/long-name/newc.cpio   |   1 +
 .../resources/synthetic/long-name/odc-fail.cpio    | Bin 0 -> 78 bytes
 src/test/resources/synthetic/long-name/odc.cpio    |   1 +
 .../resources/synthetic/long-name/pax-fail.tar     | Bin 0 -> 528 bytes
 src/test/resources/synthetic/long-name/pax.tar     | Bin 0 -> 35328 bytes
 41 files changed, 769 insertions(+), 173 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7f46fc8c5..0c0863672 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -123,6 +123,7 @@ The <action> type attribute can be add,update,fix,remove.
       <action type="add" dev="ggregory" due-to="Gary Gregory, Piotr P. Karwasz">Introduce builders for all ArchiveInputStream implementations and deprecate some constructors.</action>
       <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 P. Karwasz">Add a configurable maxEntryNameLength option to all archivers.</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="ppkarwasz" due-to="Raeps">Extract duplicate code in org.apache.commons.compress.harmony.pack200.IntList.</action>
diff --git a/src/main/java/org/apache/commons/compress/archivers/AbstractArchiveBuilder.java b/src/main/java/org/apache/commons/compress/archivers/AbstractArchiveBuilder.java
new file mode 100644
index 000000000..67f713c9d
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/AbstractArchiveBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.commons.io.build.AbstractStreamBuilder;
+
+/**
+ * Base builder for all archive formats.
+ *
+ * @param <T> The type of archive stream or file to build.
+ * @param <B> The type of the concrete builder subclass.
+ * @since 1.29.0
+ */
+public abstract class AbstractArchiveBuilder<T, B extends AbstractArchiveBuilder<T, B>>
+        extends AbstractStreamBuilder<T, B> {
+
+    private int maxEntryNameLength = Short.MAX_VALUE;
+
+    /**
+     * Constructs a new instance.
+     */
+    protected AbstractArchiveBuilder() {
+        // empty
+    }
+
+    /**
+     * Sets the maximum length, in bytes, of an archive entry name.
+     *
+     * <p>Most operating systems and file systems impose relatively small limits on
+     * file name or path length, which are sufficient for everyday use. By contrast,
+     * many archive formats permit much longer names: for example, TAR can encode
+     * names of several gigabytes, while ZIP allows up to 64&nbsp;KiB.</p>
+     *
+     * <p>This setting applies an upper bound on entry name length after encoding
+     * with the {@link #setCharset configured charset}. If an entry name exceeds this
+     * limit, an {@link ArchiveException} will be thrown during reading.</p>
+     *
+     * <p>The default is {@link Short#MAX_VALUE}, which already exceeds the limits
+     * of most operating systems.</p>
+     *
+     * @param maxEntryNameLength The maximum entry name length in bytes; must be positive
+     * @return {@code this} instance.
+     * @throws IllegalArgumentException If {@code maxEntryNameLength} is not positive.
+     */
+    public B setMaxEntryNameLength(final int maxEntryNameLength) {
+        if (maxEntryNameLength <= 0) {
+            throw new IllegalArgumentException("maxEntryNameLength must be positive");
+        }
+        this.maxEntryNameLength = maxEntryNameLength;
+        return asThis();
+    }
+
+    /**
+     * Gets the maximum length of an archive entry name.
+     *
+     * @return The maximum length of an archive entry name.
+     */
+    public int getMaxEntryNameLength() {
+        return maxEntryNameLength;
+    }
+}
diff --git a/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java
index 73c4dbefd..6653ec072 100644
--- a/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/ArchiveInputStream.java
@@ -26,7 +26,6 @@
 import java.util.Objects;
 
 import org.apache.commons.io.Charsets;
-import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.IOConsumer;
 import org.apache.commons.io.function.IOIterator;
 import org.apache.commons.io.input.NullInputStream;
@@ -52,24 +51,6 @@
  */
 public abstract class ArchiveInputStream<E extends ArchiveEntry> extends FilterInputStream {
 
-    /**
-     * Generic builder for ArchiveInputStream instances.
-     *
-     * @param <T> The type of {@link ArchiveInputStream} to build.
-     * @param <B> The type of the concrete AbstractBuilder.
-     * @since 1.29.0
-     */
-    public abstract static class AbstractBuilder<T extends ArchiveInputStream<?>, B extends AbstractBuilder<T, B>>
-            extends AbstractStreamBuilder<T, B> {
-
-        /**
-         * Constructs a new instance.
-         */
-        protected AbstractBuilder() {
-            // empty
-        }
-    }
-
     /**
      * An iterator over a collection of a specific {@link ArchiveEntry} type.
      */
@@ -114,37 +95,56 @@ public Iterator<E> unwrap() {
     /** The number of bytes read in this stream. */
     private long bytesRead;
 
-    private Charset charset;
+    private final Charset charset;
+
+    private final int maxEntryNameLength;
 
     /**
      * Constructs a new instance.
      */
     @SuppressWarnings("resource")
     public ArchiveInputStream() {
-        this(new NullInputStream(), Charset.defaultCharset());
+        this(new NullInputStream(), Charset.defaultCharset().name());
     }
 
     /**
      * Constructs a new instance.
      *
      * @param inputStream the underlying input stream, or {@code null} if this instance is to be created without an underlying stream.
-     * @param charset charset.
+     * @param charsetName charset name.
+     * @since 1.26.0
+     */
+    protected ArchiveInputStream(final InputStream inputStream, final String charsetName) {
+        super(inputStream == null ? new NullInputStream() : inputStream);
+        this.charset = Charsets.toCharset(charsetName);
+        this.maxEntryNameLength = Short.MAX_VALUE;
+    }
+
+    /**
+     * Constructs a new instance from a builder.
+     *
+     * @param builder The builder.
      * @since 1.29.0
      */
-    protected ArchiveInputStream(final InputStream inputStream, final Charset charset) {
-        super(inputStream);
-        this.charset = Charsets.toCharset(charset);
+    protected ArchiveInputStream(AbstractArchiveBuilder<?, ?> builder) throws IOException {
+        this(builder.getInputStream(), builder);
     }
 
     /**
-     * Constructs a new instance.
+     * Constructs a new instance from a builder and an input stream.
      *
-     * @param inputStream the underlying input stream, or {@code null} if this instance is to be created without an underlying stream.
-     * @param charsetName charset name.
-     * @since 1.26.0
+     * <p><strong>Note:</strong> This overload exists to support legacy constructors that did not declare
+     * {@link IOException}. For new constructors, prefer
+     * {@link #ArchiveInputStream(AbstractArchiveBuilder)} and propagate I/O errors to callers.</p>
+     *
+     * @param inputStream The underlying input stream, or {@code null} if this instance is to be created without an underlying stream.
+     * @param builder The builder.
+     * @since 1.29.0
      */
-    protected ArchiveInputStream(final InputStream inputStream, final String charsetName) {
-        this(inputStream, Charsets.toCharset(charsetName));
+    protected ArchiveInputStream(final InputStream inputStream, final AbstractArchiveBuilder<?, ?> builder) {
+        super(inputStream);
+        this.charset = builder.getCharset();
+        this.maxEntryNameLength = builder.getMaxEntryNameLength();
     }
 
     /**
@@ -229,6 +229,16 @@ public int getCount() {
         return (int) bytesRead;
     }
 
+    /**
+     * Gets the maximum length of an archive entry name.
+     *
+     * @return The maximum length of an archive entry name.
+     * @since 1.29.0
+     */
+    protected int getMaxEntryNameLength() {
+        return maxEntryNameLength;
+    }
+
     /**
      * Gets the next Archive Entry in this Stream.
      *
diff --git a/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
index 9423d8a64..40fdedaea 100644
--- a/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
@@ -25,6 +25,8 @@
 import java.util.Arrays;
 import java.util.regex.Pattern;
 
+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.ArchiveInputStream;
 import org.apache.commons.compress.utils.ArchiveUtils;
@@ -52,7 +54,7 @@ public class ArArchiveInputStream extends ArchiveInputStream<ArArchiveEntry> {
      *
      * @since 1.29.0
      */
-    public static final class Builder extends AbstractBuilder<ArArchiveInputStream, Builder> {
+    public static final class Builder extends AbstractArchiveBuilder<ArArchiveInputStream, Builder> {
 
         private Builder() {
             setCharset(StandardCharsets.US_ASCII);
@@ -167,7 +169,7 @@ public static boolean matches(final byte[] buffer, final int ignored) {
     private final byte[] metaData = new byte[NAME_LEN + LAST_MODIFIED_LEN + USER_ID_LEN + GROUP_ID_LEN + FILE_MODE_LEN + LENGTH_LEN];
 
     private ArArchiveInputStream(final Builder builder) throws IOException {
-        this(builder.getInputStream(), builder);
+        super(builder);
     }
 
     /**
@@ -180,7 +182,7 @@ public ArArchiveInputStream(final InputStream inputStream) {
     }
 
     private ArArchiveInputStream(final InputStream inputStream, final Builder builder) {
-        super(inputStream, builder.getCharset());
+        super(inputStream, builder);
     }
 
     private int asInt(final byte[] byteArray, final int offset, final int len, final boolean treatBlankAsZero) throws IOException {
@@ -244,7 +246,8 @@ public void close() throws IOException {
      * @since 1.3
      */
     private String getBSDLongName(final String bsdLongName) throws IOException {
-        final int nameLen = ParsingUtils.parseIntValue(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
+        final int nameLen =
+                checkEntryNameLength(ParsingUtils.parseIntValue(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN)));
         final byte[] name = IOUtils.readRange(in, nameLen);
         final int read = name.length;
         count(read);
@@ -276,7 +279,7 @@ private String getExtendedName(final int offset) throws IOException {
                 }
                 // Check there is a something to return, otherwise break out of the loop
                 if (i > offset) {
-                    return ArchiveUtils.toAsciiString(namebuffer, offset, i - offset);
+                    return ArchiveUtils.toAsciiString(namebuffer, offset, checkEntryNameLength(i - offset));
                 }
                 break;
             }
@@ -284,6 +287,10 @@ private String getExtendedName(final int offset) throws IOException {
         throw new ArchiveException("Failed to read GNU long file name at offset " + offset);
     }
 
+    private int checkEntryNameLength(final int nameLength) throws ArchiveException, MemoryLimitException {
+        return ArchiveUtils.checkEntryNameLength(nameLength, getMaxEntryNameLength(), "AR");
+    }
+
     /**
      * Returns the next AR entry in this stream.
      *
diff --git a/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
index 44aca3759..3c7ca4df9 100644
--- a/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/arj/ArjArchiveInputStream.java
@@ -27,9 +27,11 @@
 import java.util.ArrayList;
 import java.util.zip.CRC32;
 
+import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
 import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.commons.io.input.BoundedInputStream;
 import org.apache.commons.io.input.ChecksumInputStream;
@@ -60,7 +62,7 @@ public class ArjArchiveInputStream extends ArchiveInputStream<ArjArchiveEntry> {
      *
      * @since 1.29.0
      */
-    public static final class Builder extends AbstractBuilder<ArjArchiveInputStream, Builder> {
+    public static final class Builder extends AbstractArchiveBuilder<ArjArchiveInputStream, Builder> {
 
         private Builder() {
             setCharset(ENCODING_NAME);
@@ -117,7 +119,7 @@ public ArjArchiveInputStream(final InputStream inputStream) throws ArchiveExcept
     }
 
     private ArjArchiveInputStream(final InputStream inputStream, final Builder builder) throws ArchiveException {
-        super(new DataInputStream(inputStream), builder.getCharset());
+        super(new DataInputStream(inputStream), builder);
         dis = (DataInputStream) in;
         try {
             mainHeader = readMainHeader();
@@ -314,8 +316,8 @@ private LocalFileHeader readLocalFileHeader() throws IOException {
 
                 readExtraData(firstHeaderSize, firstHeader, localFileHeader);
 
-                localFileHeader.name = readString(basicHeader);
-                localFileHeader.comment = readString(basicHeader);
+                localFileHeader.name = readEntryName(basicHeader);
+                localFileHeader.comment = readComment(basicHeader);
 
                 final ArrayList<byte[]> extendedHeaders = new ArrayList<>();
                 int extendedHeaderSize;
@@ -374,8 +376,8 @@ private MainHeader readMainHeader() throws IOException {
             firstHeader.readUnsignedByte();
         }
 
-        header.name = readString(basicHeader);
-        header.comment = readString(basicHeader);
+        header.name = readEntryName(basicHeader);
+        header.comment = readComment(basicHeader);
 
         final int extendedHeaderSize = read16(dis);
         if (extendedHeaderSize > 0) {
@@ -400,13 +402,23 @@ private byte[] readRange(final InputStream in, final int len) throws IOException
         return b;
     }
 
-    private String readString(final DataInputStream dataIn) throws IOException {
+    private String readComment(DataInputStream dataIn) throws IOException {
+        return new String(readString(dataIn).toByteArray(), getCharset());
+    }
+
+    private String readEntryName(DataInputStream dataIn) throws IOException {
+        final ByteArrayOutputStream buffer = readString(dataIn);
+        ArchiveUtils.checkEntryNameLength(buffer.size(), getMaxEntryNameLength(), "ARJ");
+        return new String(buffer.toByteArray(), getCharset());
+    }
+
+    private ByteArrayOutputStream readString(DataInputStream dataIn) throws IOException {
         try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
             int nextByte;
             while ((nextByte = dataIn.readUnsignedByte()) != 0) {
                 buffer.write(nextByte);
             }
-            return buffer.toString(getCharset().name());
+            return buffer;
         }
     }
 }
diff --git a/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
index d99695407..03fd0613b 100644
--- a/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStream.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 import org.apache.commons.compress.archivers.zip.ZipEncoding;
@@ -79,7 +80,7 @@ public class CpioArchiveInputStream extends ArchiveInputStream<CpioArchiveEntry>
      *
      * @since 1.29.0
      */
-    public static final class Builder extends AbstractBuilder<CpioArchiveInputStream, Builder> {
+    public static final class Builder extends AbstractArchiveBuilder<CpioArchiveInputStream, Builder> {
 
         private int blockSize = BLOCK_SIZE;
 
@@ -211,7 +212,7 @@ public CpioArchiveInputStream(final InputStream in) {
     }
 
     private CpioArchiveInputStream(final InputStream in, final Builder builder) {
-        super(in, builder.getCharset());
+        super(in, builder);
         if (builder.blockSize <= 0) {
             throw new IllegalArgumentException("blockSize must be bigger than 0");
         }
@@ -423,9 +424,10 @@ private long readBinaryLong(final int length, final boolean swapHalfWord) throws
         return CpioUtil.byteArray2long(tmp, swapHalfWord);
     }
 
-    private String readCString(final int length) throws IOException {
+    private String readEntryName(int lengthWithNull) throws IOException {
+        final int length = ArchiveUtils.checkEntryNameLength(lengthWithNull - 1, getMaxEntryNameLength(), "CPIO");
         // don't include trailing NUL in file name to decode
-        final byte[] tmpBuffer = readRange(length - 1);
+        final byte[] tmpBuffer = readRange(length);
         if (in.read() == -1) {
             throw new EOFException();
         }
@@ -470,7 +472,7 @@ private CpioArchiveEntry readNewEntry(final boolean hasCrc) throws IOException {
             throw new ArchiveException("Found illegal entry with negative name length");
         }
         newEntry.setChksum(readAsciiLong(8, 16));
-        final String name = readCString(ArchiveException.toIntExact(namesize));
+        final String name = readEntryName(ArchiveException.toIntExact(namesize));
         newEntry.setName(name);
         if (CpioUtil.fileType(mode) == 0 && !name.equals(CPIO_TRAILER)) {
             throw new ArchiveException(
@@ -504,7 +506,7 @@ private CpioArchiveEntry readOldAsciiEntry() throws IOException {
         if (ret.getSize() < 0) {
             throw new ArchiveException("Found illegal entry with negative length");
         }
-        final String name = readCString(ArchiveException.toIntExact(nameSize));
+        final String name = readEntryName(ArchiveException.toIntExact(nameSize));
         ret.setName(name);
         if (CpioUtil.fileType(mode) == 0 && !name.equals(CPIO_TRAILER)) {
             throw new ArchiveException(
@@ -534,7 +536,7 @@ private CpioArchiveEntry readOldBinaryEntry(final boolean swapHalfWord) throws I
         if (oldEntry.getSize() < 0) {
             throw new ArchiveException("Found illegal entry with negative length");
         }
-        final String name = readCString(ArchiveException.toIntExact(nameSize));
+        final String name = readEntryName(ArchiveException.toIntExact(nameSize));
         oldEntry.setName(name);
         if (CpioUtil.fileType(mode) == 0 && !name.equals(CPIO_TRAILER)) {
             throw new ArchiveException(
diff --git a/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
index 5bf0b1202..4a492cf6d 100644
--- a/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/dump/DumpArchiveInputStream.java
@@ -29,10 +29,13 @@
 import java.util.Queue;
 import java.util.Stack;
 
+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.ArchiveInputStream;
 import org.apache.commons.compress.archivers.zip.ZipEncoding;
 import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
+import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.IOUtils;
 
 /**
@@ -60,7 +63,7 @@ public class DumpArchiveInputStream extends ArchiveInputStream<DumpArchiveEntry>
      *
      * @since 1.29.0
      */
-    public static final class Builder extends AbstractBuilder<DumpArchiveInputStream, Builder> {
+    public static final class Builder extends AbstractArchiveBuilder<DumpArchiveInputStream, Builder> {
 
         private Builder() {
         }
@@ -153,7 +156,7 @@ public DumpArchiveInputStream(final InputStream is) throws ArchiveException {
     }
 
     private DumpArchiveInputStream(final InputStream is, final Builder builder) throws ArchiveException {
-        super(is, builder.getCharset());
+        super(is, builder);
         this.raw = new TapeInputStream(is);
         this.hasHitEOF = false;
         this.zipEncoding = ZipEncodingHelper.getZipEncoding(builder.getCharset());
@@ -252,7 +255,7 @@ public DumpArchiveEntry getNextEntry() throws IOException {
 
         // is there anything in the queue?
         if (!queue.isEmpty()) {
-            return queue.remove();
+            return checkEntry(queue.remove());
         }
 
         while (entry == null) {
@@ -335,7 +338,7 @@ public DumpArchiveEntry getNextEntry() throws IOException {
         entry.setSimpleName(names.get(entry.getIno()).getName());
         entry.setOffset(filepos);
 
-        return entry;
+        return checkEntry(entry);
     }
 
     /**
@@ -547,7 +550,8 @@ private void readDirectoryEntry(DumpArchiveEntry entry) throws IOException {
 
                 final byte type = blockBuffer[i + 6];
 
-                final String name = DumpArchiveUtil.decode(zipEncoding, blockBuffer, i + 8, blockBuffer[i + 7]);
+                final String name =
+                        DumpArchiveUtil.decode(zipEncoding, blockBuffer, i + 8, Byte.toUnsignedInt(blockBuffer[i + 7]));
 
                 if (CURRENT_PATH_SEGMENT.equals(name) || PARENT_PATH_SEGMENT.equals(name)) {
                     // do nothing...
@@ -590,4 +594,8 @@ private void readDirectoryEntry(DumpArchiveEntry entry) throws IOException {
         }
     }
 
+    private DumpArchiveEntry checkEntry(DumpArchiveEntry entry) throws ArchiveException, MemoryLimitException {
+        ArchiveUtils.checkEntryNameLength(entry.getName().length(), getMaxEntryNameLength(), "DUMP");
+        return entry;
+    }
 }
diff --git a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
index 28dfddb27..68fbfae3b 100644
--- a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
@@ -51,12 +51,13 @@
 import java.util.zip.CheckedInputStream;
 
 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.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.commons.compress.utils.InputStreamStatistics;
 import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
 import org.apache.commons.io.build.AbstractOrigin.ByteArrayOrigin;
-import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.input.BoundedInputStream;
 import org.apache.commons.io.input.ChecksumInputStream;
 import org.apache.commons.lang3.ArrayUtils;
@@ -178,7 +179,7 @@ public String toString() {
      *
      * @since 1.26.0
      */
-    public static class Builder extends AbstractStreamBuilder<SevenZFile, Builder> {
+    public static class Builder extends AbstractArchiveBuilder<SevenZFile, Builder> {
 
         static final int MEMORY_LIMIT_KIB = Integer.MAX_VALUE;
         static final boolean USE_DEFAULTNAME_FOR_UNNAMED_ENTRIES = false;
@@ -218,7 +219,7 @@ public SevenZFile get() throws IOException {
             }
             final boolean closeOnError = seekableByteChannel != null;
             return new SevenZFile(actualChannel, actualDescription, password, closeOnError, maxMemoryLimitKiB, useDefaultNameForUnnamedEntries,
-                    tryToRecoverBrokenArchives);
+                    tryToRecoverBrokenArchives, getMaxEntryNameLength());
         }
 
         /**
@@ -486,6 +487,8 @@ public static int toNonNegativeInt(final String description, final long value) t
 
     private final boolean tryToRecoverBrokenArchives;
 
+    private final int maxEntryNameLength;
+
     /**
      * Reads a file as unencrypted 7z archive.
      *
@@ -677,14 +680,23 @@ public SevenZFile(final SeekableByteChannel channel, final String fileName, fina
         this(channel, fileName, password, false, SevenZFileOptions.DEFAULT);
     }
 
-    private SevenZFile(final SeekableByteChannel channel, final String fileName, final byte[] password, final boolean closeOnError, final int maxMemoryLimitKiB,
-            final boolean useDefaultNameForUnnamedEntries, final boolean tryToRecoverBrokenArchives) throws IOException {
+    private SevenZFile(
+            final SeekableByteChannel channel,
+            final String fileName,
+            final byte[] password,
+            final boolean closeOnError,
+            final int maxMemoryLimitKiB,
+            final boolean useDefaultNameForUnnamedEntries,
+            final boolean tryToRecoverBrokenArchives,
+            final int maxEntryNameLength)
+            throws IOException {
         boolean succeeded = false;
         this.channel = channel;
         this.fileName = fileName;
         this.maxMemoryLimitKiB = maxMemoryLimitKiB;
         this.useDefaultNameForUnnamedEntries = useDefaultNameForUnnamedEntries;
         this.tryToRecoverBrokenArchives = tryToRecoverBrokenArchives;
+        this.maxEntryNameLength = maxEntryNameLength;
         try {
             archive = readHeaders(password);
             if (password != null) {
@@ -717,7 +729,7 @@ private SevenZFile(final SeekableByteChannel channel, final String fileName, fin
     private SevenZFile(final SeekableByteChannel channel, final String fileName, final byte[] password, final boolean closeOnError,
             final SevenZFileOptions options) throws IOException {
         this(channel, fileName, password, closeOnError, options.getMaxMemoryLimitInKb(), options.getUseDefaultNameForUnnamedEntries(),
-                options.getTryToRecoverBrokenArchives());
+                options.getTryToRecoverBrokenArchives(), Short.MAX_VALUE);
     }
 
     /**
@@ -1353,7 +1365,10 @@ private void readFilesInfo(final ByteBuffer header, final Archive archive) throw
                 for (int i = 0; i < namesLength; i += 2) {
                     if (names[i] == 0 && names[i + 1] == 0) {
                         computeIfAbsent(fileMap, nextFile);
-                        fileMap.get(nextFile).setName(new String(names, nextName, i - nextName, UTF_16LE));
+                        // Entry name length in UTF-16LE characters (not bytes)
+                        final int entryNameLength =
+                                ArchiveUtils.checkEntryNameLength((i - nextName) / 2, maxEntryNameLength, "7z");
+                        fileMap.get(nextFile).setName(new String(names, nextName, 2 * entryNameLength, UTF_16LE));
                         nextName = i + 2;
                         nextFile++;
                     }
diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java b/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java
index 5555f5ccc..0f471a1ca 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/AbstractTarBuilder.java
@@ -19,7 +19,7 @@
 
 package org.apache.commons.compress.archivers.tar;
 
-import org.apache.commons.io.build.AbstractStreamBuilder;
+import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
 
 /**
  * Abstracts TAR builder operations.
@@ -28,7 +28,7 @@
  * @param <B> the type of builder subclass.
  * @since 1.29.0
  */
-public abstract class AbstractTarBuilder<T, B extends AbstractTarBuilder<T, B>> extends AbstractStreamBuilder<T, B> {
+public abstract class AbstractTarBuilder<T, B extends AbstractTarBuilder<T, B>> extends AbstractArchiveBuilder<T, B> {
 
     private int blockSize = TarConstants.DEFAULT_BLKSIZE;
     private int recordSize = TarConstants.DEFAULT_RCDSIZE;
diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
index 80dd6859f..6f3140983 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
@@ -25,6 +25,7 @@
 package org.apache.commons.compress.archivers.tar;
 
 import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -204,7 +205,7 @@ public TarArchiveInputStream(final InputStream inputStream, final boolean lenien
     }
 
     private TarArchiveInputStream(final InputStream inputStream, final Builder builder) {
-        super(inputStream, builder.getCharset());
+        super(inputStream, builder);
         this.zipEncoding = ZipEncodingHelper.getZipEncoding(builder.getCharset());
         this.recordBuffer = new byte[builder.getRecordSize()];
         this.blockSize = builder.getBlockSize();
@@ -512,7 +513,15 @@ public TarArchiveEntry getNextEntry() throws IOException {
             lastWasSpecial = TarUtils.isSpecialTarRecord(currEntry);
             if (lastWasSpecial) {
                 // Handle PAX, GNU long name, or other special records
-                TarUtils.handleSpecialTarRecord(this, zipEncoding, currEntry, paxHeaders, sparseHeaders, globalPaxHeaders, globalSparseHeaders);
+                TarUtils.handleSpecialTarRecord(
+                        this,
+                        zipEncoding,
+                        getMaxEntryNameLength(),
+                        currEntry,
+                        paxHeaders,
+                        sparseHeaders,
+                        globalPaxHeaders,
+                        globalSparseHeaders);
             }
         } while (lastWasSpecial);
         // Apply global and local PAX headers
@@ -660,7 +669,7 @@ public int read(final byte[] buf, final int offset, int numToRead) throws IOExce
         }
         if (totalRead == -1) {
             if (numToRead > 0) {
-                throw new ArchiveException("Truncated TAR archive");
+                throw new EOFException("Truncated TAR archive");
             }
             setAtEOF(true);
         } else {
diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java
index 9ebb6c1d6..3649320c0 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarFile.java
@@ -19,6 +19,7 @@
 package org.apache.commons.compress.archivers.tar;
 
 import java.io.Closeable;
+import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -65,7 +66,7 @@ private final class BoundedTarEntryInputStream extends BoundedArchiveInputStream
         BoundedTarEntryInputStream(final TarArchiveEntry entry, final SeekableByteChannel channel) throws IOException {
             super(entry.getDataOffset(), entry.getRealSize());
             if (channel.size() - entry.getSize() < entry.getDataOffset()) {
-                throw new ArchiveException("Entry size exceeds archive size");
+                throw new EOFException("Truncated TAR archive: entry size exceeds archive size");
             }
             this.entry = entry;
             this.channel = channel;
@@ -84,7 +85,7 @@ protected int read(final long pos, final ByteBuffer buf) throws IOException {
             }
             if (totalRead == -1) {
                 if (buf.array().length > 0) {
-                    throw new ArchiveException("Truncated TAR archive");
+                    throw new EOFException("Truncated TAR archive");
                 }
                 setAtEOF(true);
             } else {
@@ -233,6 +234,8 @@ public static Builder builder() {
 
     private final Map<String, List<InputStream>> sparseInputStreams = new HashMap<>();
 
+    private final int maxEntryNameLength;
+
     private TarFile(final Builder builder) throws IOException {
         this.archive = builder.channel != null ? builder.channel : Files.newByteChannel(builder.getPath());
         this.zipEncoding = ZipEncodingHelper.getZipEncoding(builder.getCharset());
@@ -240,6 +243,7 @@ private TarFile(final Builder builder) throws IOException {
         this.recordBuffer = ByteBuffer.allocate(this.recordSize);
         this.blockSize = builder.getBlockSize();
         this.lenient = builder.isLenient();
+        this.maxEntryNameLength = builder.getMaxEntryNameLength();
         forEach(entries::add);
     }
 
@@ -507,7 +511,14 @@ private TarArchiveEntry getNextTarEntry() throws IOException {
             lastWasSpecial = TarUtils.isSpecialTarRecord(currEntry);
             if (lastWasSpecial) {
                 // Handle PAX, GNU long name, or other special records
-                TarUtils.handleSpecialTarRecord(getInputStream(currEntry), zipEncoding, currEntry, paxHeaders, sparseHeaders, globalPaxHeaders,
+                TarUtils.handleSpecialTarRecord(
+                        getInputStream(currEntry),
+                        zipEncoding,
+                        maxEntryNameLength,
+                        currEntry,
+                        paxHeaders,
+                        sparseHeaders,
+                        globalPaxHeaders,
                         globalSparseHeaders);
             }
         } while (lastWasSpecial);
diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
index 3938ce2f3..b121d6f7c 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
@@ -18,6 +18,7 @@
  */
 package org.apache.commons.compress.archivers.tar;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
@@ -33,15 +34,14 @@
 import java.util.Map;
 import java.util.regex.Pattern;
 
+import org.apache.commons.compress.MemoryLimitException;
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.zip.ZipEncoding;
 import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
 import org.apache.commons.compress.utils.ArchiveUtils;
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.commons.compress.utils.ParsingUtils;
-import org.apache.commons.io.input.BoundedInputStream;
 import org.apache.commons.io.output.ByteArrayOutputStream;
-import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
 
 /**
  * This class provides static utility methods to work with byte streams.
@@ -354,6 +354,7 @@ public static void formatUnsignedOctalString(final long value, final byte[] buff
      *
      * @param input the input stream from which to read the special tar entry content.
      * @param encoding the encoding to use for reading names.
+     * @param maxEntryNameLength the maximum allowed length for entry names.
      * @param entry the tar entry to handle.
      * @param paxHeaders the map to update with PAX headers.
      * @param sparseHeaders the list to update with sparse headers.
@@ -361,27 +362,36 @@ public static void formatUnsignedOctalString(final long value, final byte[] buff
      * @param globalSparseHeaders the list to update with global sparse headers.
      * @throws IOException if an I/O error occurs while reading the entry.
      */
-    static void handleSpecialTarRecord(final InputStream input, final ZipEncoding encoding, final TarArchiveEntry entry, final Map<String, String> paxHeaders,
-            final List<TarArchiveStructSparse> sparseHeaders, final Map<String, String> globalPaxHeaders,
-            final List<TarArchiveStructSparse> globalSparseHeaders) throws IOException {
+    static void handleSpecialTarRecord(
+            final InputStream input,
+            final ZipEncoding encoding,
+            final int maxEntryNameLength,
+            final TarArchiveEntry entry,
+            final Map<String, String> paxHeaders,
+            final List<TarArchiveStructSparse> sparseHeaders,
+            final Map<String, String> globalPaxHeaders,
+            final List<TarArchiveStructSparse> globalSparseHeaders)
+            throws IOException {
         if (entry.isGNULongLinkEntry()) {
             // GNU long link entry: read and store the link path
-            final String longLinkName = readLongName(input, encoding, entry);
+            final String longLinkName = readLongName(input, encoding, maxEntryNameLength, entry);
             paxHeaders.put("linkpath", longLinkName);
         } else if (entry.isGNULongNameEntry()) {
             // GNU long name entry: read and store the file path
-            final String longName = readLongName(input, encoding, entry);
+            final String longName = readLongName(input, encoding, maxEntryNameLength, entry);
             paxHeaders.put("path", longName);
         } else if (entry.isGlobalPaxHeader()) {
             // Global PAX header: clear and update global PAX and sparse headers
             globalSparseHeaders.clear();
             globalPaxHeaders.clear();
-            globalPaxHeaders.putAll(parsePaxHeaders(input, globalSparseHeaders, globalPaxHeaders, entry.getSize()));
+            globalPaxHeaders.putAll(
+                    parsePaxHeaders(input, globalPaxHeaders, entry.getSize(), maxEntryNameLength, globalSparseHeaders));
         } else if (entry.isPaxHeader()) {
             // PAX header: clear and update local PAX and sparse headers, parse GNU sparse headers if present
             sparseHeaders.clear();
             paxHeaders.clear();
-            paxHeaders.putAll(parsePaxHeaders(input, sparseHeaders, globalPaxHeaders, entry.getSize()));
+            paxHeaders.putAll(
+                    parsePaxHeaders(input, globalPaxHeaders, entry.getSize(), maxEntryNameLength, sparseHeaders));
             if (paxHeaders.containsKey(TarGnuSparseKeys.MAP)) {
                 sparseHeaders.addAll(parseFromPAX01SparseHeaders(paxHeaders.get(TarGnuSparseKeys.MAP)));
             }
@@ -709,7 +719,7 @@ protected static List<TarArchiveStructSparse> parsePAX1XSparseHeaders(final Inpu
     @Deprecated
     protected static Map<String, String> parsePaxHeaders(final InputStream inputStream, final List<TarArchiveStructSparse> sparseHeaders,
             final Map<String, String> globalPaxHeaders) throws IOException {
-        return parsePaxHeaders(inputStream, sparseHeaders, globalPaxHeaders, -1);
+        return parsePaxHeaders(inputStream, globalPaxHeaders, -1, Short.MAX_VALUE, sparseHeaders);
     }
 
     /**
@@ -731,7 +741,7 @@ protected static Map<String, String> parsePaxHeaders(final InputStream inputStre
      * </p>
      *
      * @param inputStream      input stream to read keys and values
-     * @param sparseHeaders    used in PAX Format 0.0 &amp; 0.1, as it may appear multiple times, the sparse headers need to be stored in an array, not a map
+     * @param sparseHeaders    used in PAX Format 0.0, as it may appear multiple times, the sparse headers need to be stored in an array, not a map
      * @param globalPaxHeaders global PAX headers of the tar archive
      * @param headerSize       total size of the PAX header, will be ignored if negative
      * @return map of PAX headers values found inside the current (local or global) PAX headers tar entry.
@@ -740,6 +750,42 @@ protected static Map<String, String> parsePaxHeaders(final InputStream inputStre
      */
     protected static Map<String, String> parsePaxHeaders(final InputStream inputStream, final List<TarArchiveStructSparse> sparseHeaders,
             final Map<String, String> globalPaxHeaders, final long headerSize) throws IOException {
+        return parsePaxHeaders(inputStream, globalPaxHeaders, headerSize, Short.MAX_VALUE, sparseHeaders);
+    }
+
+    /**
+     * Parses and processes the contents of a PAX header block.
+     *
+     * <p>This method reads key–value pairs from the given input stream, applies
+     * them to the provided global PAX headers, and performs additional handling:</p>
+     *
+     * <ul>
+     *   <li>Validates that entry path lengths do not exceed the specified maximum.</li>
+     *   <li>Extracts GNU sparse headers (format 0.0) if present, adding them to the
+     *       {@code sparseHeaders} list. These headers may occur multiple times and
+     *       do not follow the key–value format of standard PAX entries.</li>
+     * </ul>
+     *
+     * @param inputStream        The input stream providing PAX header data
+     * @param globalPaxHeaders   The global PAX headers of the tar archive
+     * @param headerSize         The total size of the PAX header block; ignored if negative
+     * @param maxEntryPathLength The maximum permitted length for entry paths
+     * @param sparseHeaders      Output list to collect any GNU sparse 0.0 headers found
+     * @return A map of PAX headers merged with the supplied global headers
+     * @throws EOFException          If the stream ends unexpectedly
+     * @throws MemoryLimitException  If the headers exceed memory limits
+     * @throws ArchiveException      If a header is malformed or contains invalid data
+     * @throws IOException           If an I/O error occurs while reading
+     */
+    private static Map<String, String> parsePaxHeaders(
+            final InputStream inputStream,
+            final Map<String, String> globalPaxHeaders,
+            final long headerSize,
+            final int maxEntryPathLength,
+            final List<? super TarArchiveStructSparse> sparseHeaders)
+            throws IOException {
+        // Check if there is enough memory to store the headers
+        MemoryLimitException.checkBytes(headerSize, Long.MAX_VALUE);
         final Map<String, String> headers = new HashMap<>(globalPaxHeaders);
         Long offset = null;
         // Format is "length keyword=value\n";
@@ -767,15 +813,24 @@ protected static Map<String, String> parsePaxHeaders(final InputStream inputStre
                             final String keyword = coll.toString(StandardCharsets.UTF_8);
                             // Get rest of entry
                             final int restLen = len - read;
+
+                            // Validate entry length
+                            // 1. Ignore empty keywords
                             if (restLen <= 1) { // only NL
                                 headers.remove(keyword);
+                            // 2. Entry length exceeds header size
                             } else if (headerSize >= 0 && restLen > headerSize - totalRead) {
                                 throw new ArchiveException("PAX header value size %,d exceeds size of header record.", restLen);
                             } else {
+                                // 3. Entry length exceeds configurable file and link name limits
+                                if ("path".equals(keyword) || "linkpath".equals(keyword)) {
+                                    ArchiveUtils.checkEntryNameLength(restLen - 1, maxEntryPathLength, "TAR");
+                                }
                                 final byte[] rest = IOUtils.readRange(inputStream, restLen);
                                 final int got = rest.length;
                                 if (got != restLen) {
-                                    throw new ArchiveException("Failed to read PAX header: Expected %,d bytes, read %,d.", restLen, got);
+                                    throw new EOFException(String.format(
+                                            "Failed to read PAX header: Expected %,d bytes, read %,d.", restLen, got));
                                 }
                                 totalRead += restLen;
                                 // Drop trailing NL
@@ -888,30 +943,23 @@ private static long[] readLineOfNumberForPax1x(final InputStream inputStream) th
      * @throws IOException if an I/O error occurs or the entry is truncated.
      * @throws ArchiveException if the entry size is invalid.
      */
-    static String readLongName(final InputStream input, final ZipEncoding encoding, final TarArchiveEntry entry)
+    static String readLongName(
+            final InputStream input,
+            final ZipEncoding encoding,
+            final int maxEntryNameLength,
+            final TarArchiveEntry entry)
             throws IOException {
-        final long size = entry.getSize();
-        // The encoding requires a byte array, whose size must be a positive int.
-        if (size > Integer.MAX_VALUE) {
-            throw new ArchiveException("Invalid long name entry: size %,d exceeds maximum allowed.", entry.getSize());
-        }
-        // Read the long name incrementally to limit memory allocation in case of a corrupted entry.
-        final BoundedInputStream boundedInput = BoundedInputStream.builder()
-                .setInputStream(input)
-                .setMaxCount(size)
-                .setPropagateClose(false)
-                .get();
-        final UnsynchronizedByteArrayOutputStream outputStream = UnsynchronizedByteArrayOutputStream.builder().get();
-        final long read = org.apache.commons.io.IOUtils.copyLarge(boundedInput, outputStream);
-        if (read != size) {
-            throw new ArchiveException("Truncated long name entry: expected %,d bytes, read %,d bytes.", size, read);
-        }
-        final byte[] name = outputStream.toByteArray();
-        int length = name.length;
-        while (length > 0 && name[length - 1] == 0) {
-            length--;
-        }
-        return encoding.decode(Arrays.copyOf(name, length));
+        final int declaredLength = ArchiveUtils.checkEntryNameLength(entry.getSize(), maxEntryNameLength, "TAR");
+        final byte[] name = IOUtils.readRange(input, declaredLength);
+        int actualLength = name.length;
+        if (actualLength != declaredLength) {
+            throw new EOFException(String.format(
+                    "Truncated long name entry: expected %,d bytes, read %,d bytes.", declaredLength, actualLength));
+        }
+        while (actualLength > 0 && name[actualLength - 1] == 0) {
+            actualLength--;
+        }
+        return encoding.decode(Arrays.copyOf(name, actualLength));
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
index d02e62568..555f10465 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
@@ -42,6 +42,7 @@
 import java.util.zip.ZipException;
 
 import org.apache.commons.compress.MemoryLimitException;
+import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
 import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.ArchiveInputStream;
@@ -85,7 +86,7 @@ public class ZipArchiveInputStream extends ArchiveInputStream<ZipArchiveEntry> i
      * @since 1.29.0
      */
     public abstract static class AbstractBuilder<T extends ZipArchiveInputStream, B extends AbstractBuilder<T, B>>
-            extends ArchiveInputStream.AbstractBuilder<T, B> {
+            extends AbstractArchiveBuilder<T, B> {
 
         private boolean useUnicodeExtraFields = true;
         private boolean supportStoredEntryDataDescriptor;
@@ -421,7 +422,7 @@ public ZipArchiveInputStream(final InputStream inputStream) {
     }
 
     private ZipArchiveInputStream(final InputStream inputStream, final AbstractBuilder<?, ?> builder) {
-        super(inputStream, builder.getCharset());
+        super(inputStream, builder);
         this.in = new PushbackInputStream(inputStream, buf.capacity());
         this.zipEncoding = ZipEncodingHelper.getZipEncoding(builder.getCharset());
         this.useUnicodeExtraFields = builder.isUseUnicodeExtraFields();
@@ -861,7 +862,8 @@ public ZipArchiveEntry getNextZipEntry() throws IOException {
         } else {
             off += 3 * WORD;
         }
-        final int fileNameLen = ZipShort.getValue(lfhBuf, off);
+        final int fileNameLen =
+                ArchiveUtils.checkEntryNameLength(ZipShort.getValue(lfhBuf, off), getMaxEntryNameLength(), "ZIP");
         off += SHORT;
         final int extraLen = ZipShort.getValue(lfhBuf, off);
         final byte[] fileName = readRange(fileNameLen);
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
index 3e28361c3..2c154636e 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
@@ -51,12 +51,14 @@
 import java.util.zip.Inflater;
 import java.util.zip.ZipException;
 
+import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.compress.archivers.EntryStreamOffsets;
 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
 import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
 import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
 import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream;
+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.compress.utils.IOUtils;
@@ -65,7 +67,6 @@
 import org.apache.commons.io.Charsets;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.build.AbstractOrigin.ByteArrayOrigin;
-import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.IOFunction;
 import org.apache.commons.io.function.IOStream;
 import org.apache.commons.io.input.BoundedInputStream;
@@ -132,7 +133,7 @@ protected int read(final long pos, final ByteBuffer buf) throws IOException {
      *
      * @since 1.26.0
      */
-    public static class Builder extends AbstractStreamBuilder<ZipFile, Builder> {
+    public static class Builder extends AbstractArchiveBuilder<ZipFile, Builder> {
 
         static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
         private SeekableByteChannel seekableByteChannel;
@@ -169,8 +170,15 @@ public ZipFile get() throws IOException {
                 actualDescription = path.toString();
             }
             final boolean closeOnError = seekableByteChannel != null;
-            return new ZipFile(actualChannel, actualDescription, getCharset(), useUnicodeExtraFields, closeOnError, ignoreLocalFileHeader,
-                    zstdInputStreamFactory);
+            return new ZipFile(
+                    actualChannel,
+                    actualDescription,
+                    getCharset(),
+                    useUnicodeExtraFields,
+                    closeOnError,
+                    ignoreLocalFileHeader,
+                    zstdInputStreamFactory,
+                    getMaxEntryNameLength());
         }
 
         /**
@@ -751,6 +759,8 @@ private static boolean tryToLocateSignature(final SeekableByteChannel channel, f
 
     private long firstLocalFileHeaderOffset;
 
+    private final int maxEntryNameLength;
+
     /**
      * Opens the given file for reading, assuming "UTF8" for file names.
      *
@@ -811,7 +821,15 @@ public ZipFile(final File file, final String encoding, final boolean useUnicodeE
     @Deprecated
     @SuppressWarnings("resource") // Caller closes
     public ZipFile(final File file, final String encoding, final boolean useUnicodeExtraFields, final boolean ignoreLocalFileHeader) throws IOException {
-        this(newReadByteChannel(file.toPath()), file.getAbsolutePath(), encoding, useUnicodeExtraFields, true, ignoreLocalFileHeader);
+        this(
+                newReadByteChannel(file.toPath()),
+                file.getAbsolutePath(),
+                Charsets.toCharset(encoding),
+                useUnicodeExtraFields,
+                true,
+                ignoreLocalFileHeader,
+                null,
+                Short.MAX_VALUE);
     }
 
     /**
@@ -877,7 +895,15 @@ public ZipFile(final Path path, final String encoding, final boolean useUnicodeE
     @SuppressWarnings("resource") // Caller closes
     @Deprecated
     public ZipFile(final Path path, final String encoding, final boolean useUnicodeExtraFields, final boolean ignoreLocalFileHeader) throws IOException {
-        this(newReadByteChannel(path), path.toAbsolutePath().toString(), encoding, useUnicodeExtraFields, true, ignoreLocalFileHeader);
+        this(
+                newReadByteChannel(path),
+                path.toAbsolutePath().toString(),
+                Charsets.toCharset(encoding),
+                useUnicodeExtraFields,
+                true,
+                ignoreLocalFileHeader,
+                null,
+                Short.MAX_VALUE);
     }
 
     /**
@@ -913,14 +939,23 @@ public ZipFile(final SeekableByteChannel channel, final String encoding) throws
         this(channel, "a SeekableByteChannel", encoding, true);
     }
 
-    private ZipFile(final SeekableByteChannel channel, final String channelDescription, final Charset encoding, final boolean useUnicodeExtraFields,
-            final boolean closeOnError, final boolean ignoreLocalFileHeader, final IOFunction<InputStream, InputStream> zstdInputStream) throws IOException {
+    private ZipFile(
+            final SeekableByteChannel channel,
+            final String channelDescription,
+            final Charset encoding,
+            final boolean useUnicodeExtraFields,
+            final boolean closeOnError,
+            final boolean ignoreLocalFileHeader,
+            final IOFunction<InputStream, InputStream> zstdInputStream,
+            final int maxEntryNameLength)
+            throws IOException {
         this.isSplitZipArchive = channel instanceof ZipSplitReadOnlySeekableByteChannel;
         this.encoding = Charsets.toCharset(encoding, Builder.DEFAULT_CHARSET);
         this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
         this.useUnicodeExtraFields = useUnicodeExtraFields;
         this.archive = channel;
         this.zstdInputStreamFactory = zstdInputStream;
+        this.maxEntryNameLength = maxEntryNameLength;
         boolean success = false;
         try {
             final Map<ZipArchiveEntry, NameAndComment> entriesWithoutUTF8Flag = populateFromCentralDirectory();
@@ -929,6 +964,8 @@ private ZipFile(final SeekableByteChannel channel, final String channelDescripti
             }
             fillNameMap();
             success = true;
+        } catch (final ArchiveException e) {
+            throw e;
         } catch (final IOException e) {
             throw new ArchiveException("Error reading Zip content from " + channelDescription, (Throwable) e);
         } finally {
@@ -956,7 +993,15 @@ private ZipFile(final SeekableByteChannel channel, final String channelDescripti
     @Deprecated
     public ZipFile(final SeekableByteChannel channel, final String channelDescription, final String encoding, final boolean useUnicodeExtraFields)
             throws IOException {
-        this(channel, channelDescription, encoding, useUnicodeExtraFields, false, false);
+        this(
+                channel,
+                channelDescription,
+                Charsets.toCharset(encoding),
+                useUnicodeExtraFields,
+                false,
+                false,
+                null,
+                Short.MAX_VALUE);
     }
 
     /**
@@ -984,12 +1029,15 @@ public ZipFile(final SeekableByteChannel channel, final String channelDescriptio
     @Deprecated
     public ZipFile(final SeekableByteChannel channel, final String channelDescription, final String encoding, final boolean useUnicodeExtraFields,
             final boolean ignoreLocalFileHeader) throws IOException {
-        this(channel, channelDescription, encoding, useUnicodeExtraFields, false, ignoreLocalFileHeader);
-    }
-
-    private ZipFile(final SeekableByteChannel channel, final String channelDescription, final String encoding, final boolean useUnicodeExtraFields,
-            final boolean closeOnError, final boolean ignoreLocalFileHeader) throws IOException {
-        this(channel, channelDescription, Charsets.toCharset(encoding), useUnicodeExtraFields, closeOnError, ignoreLocalFileHeader, null);
+        this(
+                channel,
+                channelDescription,
+                Charsets.toCharset(encoding),
+                useUnicodeExtraFields,
+                false,
+                ignoreLocalFileHeader,
+                null,
+                Short.MAX_VALUE);
     }
 
     /**
@@ -1527,7 +1575,8 @@ private void readCentralDirectoryEntry(final Map<ZipArchiveEntry, NameAndComment
         ze.setSize(size);
         off += ZipConstants.WORD;
 
-        final int fileNameLen = ZipShort.getValue(cfhBuf, off);
+        final int fileNameLen =
+                ArchiveUtils.checkEntryNameLength(ZipShort.getValue(cfhBuf, off), maxEntryNameLength, "ZIP");
         off += ZipConstants.SHORT;
         if (fileNameLen < 0) {
             throw new ArchiveException("Broken archive, entry with negative fileNameLen");
diff --git a/src/main/java/org/apache/commons/compress/utils/ArchiveUtils.java b/src/main/java/org/apache/commons/compress/utils/ArchiveUtils.java
index 1b3c827b7..29cc5190b 100644
--- a/src/main/java/org/apache/commons/compress/utils/ArchiveUtils.java
+++ b/src/main/java/org/apache/commons/compress/utils/ArchiveUtils.java
@@ -22,7 +22,9 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 
+import org.apache.commons.compress.MemoryLimitException;
 import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveException;
 
 /**
  * Generic Archive utilities.
@@ -31,6 +33,31 @@ public class ArchiveUtils {
 
     private static final int MAX_SANITIZED_NAME_LENGTH = 255;
 
+    private static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
+
+    /**
+     * Checks that the given entry name length is valid.
+     *
+     * @param length The length of the entry name.
+     * @param maxEntryNameLength The maximum allowed length of the entry name.
+     * @param archiveType The type of the archive (for error messages).
+     * @return The length, if valid.
+     * @throws ArchiveException If the length is not valid.
+     * @throws MemoryLimitException If the length is valid, but too large for the current JVM to handle.
+     * @since 1.29.0
+     */
+    public static int checkEntryNameLength(long length, int maxEntryNameLength, String archiveType)
+            throws ArchiveException, MemoryLimitException {
+        if (length > maxEntryNameLength) {
+            throw new ArchiveException(
+                    "Invalid %s archive entry: invalid file name length %,d (must be between 1 and %,d). "
+                            + "If the file is not corrupt, consider increasing the `maxEntryNameLength` limit.",
+                    archiveType, length, maxEntryNameLength);
+        }
+        MemoryLimitException.checkBytes(length, SOFT_MAX_ARRAY_LENGTH);
+        return (int) length;
+    }
+
     /**
      * Tests whether true if the first N bytes of an array are all zero.
      *
diff --git a/src/test/java/org/apache/commons/compress/archivers/MaxNameEntryLengthTest.java b/src/test/java/org/apache/commons/compress/archivers/MaxNameEntryLengthTest.java
new file mode 100644
index 000000000..43d17a2f7
--- /dev/null
+++ b/src/test/java/org/apache/commons/compress/archivers/MaxNameEntryLengthTest.java
@@ -0,0 +1,348 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import org.apache.commons.compress.AbstractTest;
+import org.apache.commons.compress.MemoryLimitException;
+import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
+import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
+import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
+import org.apache.commons.compress.archivers.dump.DumpArchiveEntry;
+import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream;
+import org.apache.commons.compress.archivers.sevenz.SevenZFile;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarFile;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.io.function.IOStream;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Tests handling of file names limits in various archive formats.
+ */
+public class MaxNameEntryLengthTest extends AbstractTest {
+
+    private static final int PORTABLE_NAME_LIMIT = 1023;
+    private static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
+
+    @SuppressWarnings("OctalInteger")
+    private static final int CPIO_OLD_ASCII_NAME_LIMIT = 0777_776;
+
+    static Stream<Arguments> testTruncatedStreams() throws IOException {
+        return Stream.of(
+                Arguments.of(
+                        ArArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/bsd-fail.ar"))
+                                .get(),
+                        SOFT_MAX_ARRAY_LENGTH),
+                Arguments.of(
+                        ArArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/gnu-fail.ar"))
+                                .get(),
+                        SOFT_MAX_ARRAY_LENGTH),
+                Arguments.of(
+                        CpioArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/odc-fail.cpio"))
+                                .get(),
+                        CPIO_OLD_ASCII_NAME_LIMIT),
+                Arguments.of(
+                        CpioArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/newc-fail.cpio"))
+                                .get(),
+                        SOFT_MAX_ARRAY_LENGTH),
+                Arguments.of(
+                        CpioArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/crc-fail.cpio"))
+                                .get(),
+                        SOFT_MAX_ARRAY_LENGTH),
+                Arguments.of(
+                        TarArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/pax-fail.tar"))
+                                .get(),
+                        // The PAX entry length is the limiting factor: "2147483647 path=...\n"
+                        Integer.MAX_VALUE),
+                Arguments.of(
+                        TarArchiveInputStream.builder()
+                                .setMaxEntryNameLength(Integer.MAX_VALUE)
+                                .setURI(getURI("synthetic/long-name/gnu-fail.tar"))
+                                .get(),
+                        SOFT_MAX_ARRAY_LENGTH));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testTruncatedStreams(final ArchiveInputStream<?> archiveInputStream, final long expectedLength) {
+        // If the file name length exceeds available memory, the stream fails fast with MemoryLimitException.
+        // Otherwise, it fails with EOFException when the stream ends unexpectedly.
+        if (Runtime.getRuntime().totalMemory() < expectedLength) {
+            final MemoryLimitException exception =
+                    assertThrows(MemoryLimitException.class, archiveInputStream::getNextEntry);
+            final String message = exception.getMessage();
+            assertNotNull(message);
+            assertTrue(
+                    message.contains(String.format("%,d", expectedLength)),
+                    "Message mentions expected length (" + expectedLength + "): " + message);
+        } else {
+            assertThrows(EOFException.class, archiveInputStream::getNextEntry);
+        }
+    }
+
+    static Stream<Arguments> testTruncatedTarFiles() throws IOException {
+        return Stream.of(
+                Arguments.of(TarFile.builder()
+                        .setMaxEntryNameLength(Integer.MAX_VALUE)
+                        .setURI(getURI("synthetic/long-name/pax-fail.tar"))),
+                Arguments.of(TarFile.builder()
+                        .setMaxEntryNameLength(Integer.MAX_VALUE)
+                        .setURI(getURI("synthetic/long-name/gnu-fail.tar"))));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testTruncatedTarFiles(final TarFile.Builder tarFileBuilder) {
+        // Since the real size of the archive is known, the truncation is detected
+        // much earlier and before trying to read file names.
+        assertThrows(EOFException.class, () -> tarFileBuilder.get().getEntries());
+    }
+
+    static Stream<Arguments> testValidStreams() throws IOException {
+        return Stream.of(
+                Arguments.of(
+                        ArArchiveInputStream.builder().setURI(getURI("synthetic/long-name/bsd-short-max-value.ar")),
+                        Short.MAX_VALUE),
+                Arguments.of(
+                        ArArchiveInputStream.builder().setURI(getURI("synthetic/long-name/gnu-short-max-value.ar")),
+                        Short.MAX_VALUE),
+                Arguments.of(ArjArchiveInputStream.builder().setURI(getURI("synthetic/long-name/long-name.arj")), 2568),
+                Arguments.of(
+                        CpioArchiveInputStream.builder().setURI(getURI("synthetic/long-name/bin-big-endian.cpio")),
+                        Short.MAX_VALUE - 1),
+                Arguments.of(
+                        CpioArchiveInputStream.builder().setURI(getURI("synthetic/long-name/bin-little-endian.cpio")),
+                        Short.MAX_VALUE - 1),
+                Arguments.of(
+                        CpioArchiveInputStream.builder().setURI(getURI("synthetic/long-name/odc.cpio")),
+                        Short.MAX_VALUE),
+                Arguments.of(
+                        CpioArchiveInputStream.builder().setURI(getURI("synthetic/long-name/newc.cpio")),
+                        Short.MAX_VALUE),
+                Arguments.of(
+                        CpioArchiveInputStream.builder().setURI(getURI("synthetic/long-name/crc.cpio")),
+                        Short.MAX_VALUE),
+                Arguments.of(
+                        TarArchiveInputStream.builder().setURI(getURI("synthetic/long-name/pax.tar")), Short.MAX_VALUE),
+                Arguments.of(
+                        TarArchiveInputStream.builder().setURI(getURI("synthetic/long-name/gnu.tar")), Short.MAX_VALUE),
+                Arguments.of(
+                        ZipArchiveInputStream.builder().setURI(getURI("synthetic/long-name/long-name.zip")),
+                        Short.MAX_VALUE));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testValidStreams(final AbstractArchiveBuilder<ArchiveInputStream<?>, ?> builder, final int expectedLength)
+            throws IOException {
+        try (ArchiveInputStream<?> archiveInputStream = builder.get()) {
+            final ArchiveEntry entry = archiveInputStream.getNextEntry();
+            assertNotNull(entry);
+            final String name = entry.getName();
+            assertEquals(expectedLength, name.length(), "Unexpected name length");
+            final String expected = StringUtils.repeat("a", expectedLength);
+            assertEquals(expected, name);
+        }
+        // Impose a file name length limit and verify that it is enforced.
+        builder.setMaxEntryNameLength(PORTABLE_NAME_LIMIT);
+        try (ArchiveInputStream<?> archiveInputStream = builder.get()) {
+            final ArchiveException exception = assertThrows(ArchiveException.class, archiveInputStream::getNextEntry);
+            final String message = exception.getMessage();
+            assertNotNull(message);
+            assertTrue(message.contains("file name length"));
+            assertTrue(message.contains(String.format("%,d", expectedLength)));
+        }
+    }
+
+    static Stream<Arguments> testValidTarFiles() throws IOException {
+        return Stream.of(
+                Arguments.of(TarFile.builder().setURI(getURI("synthetic/long-name/pax.tar")), Short.MAX_VALUE),
+                Arguments.of(TarFile.builder().setURI(getURI("synthetic/long-name/gnu.tar")), Short.MAX_VALUE));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testValidTarFiles(final TarFile.Builder tarFileBuilder, final int expectedLength) throws IOException {
+        try (TarFile tarFile = tarFileBuilder.get()) {
+            for (final ArchiveEntry entry : tarFile.getEntries()) {
+                assertNotNull(entry);
+                final String name = entry.getName();
+                assertEquals(expectedLength, name.length(), "Unexpected name length");
+                final String expected = StringUtils.repeat("a", expectedLength);
+                assertEquals(expected, name);
+            }
+        }
+        // Impose a file name length limit and verify that it is enforced.
+        tarFileBuilder.setMaxEntryNameLength(PORTABLE_NAME_LIMIT);
+        final ArchiveException exception = assertThrows(ArchiveException.class, () -> tarFileBuilder.get());
+        final String message = exception.getMessage();
+        assertNotNull(message);
+        assertTrue(message.contains("file name length"));
+        assertTrue(message.contains(String.format("%,d", expectedLength)));
+    }
+
+    @Test
+    void testValidZipFile() throws IOException {
+        final ZipFile.Builder builder = ZipFile.builder().setURI(getURI("synthetic/long-name/long-name.zip"));
+        final int expectedLength = Short.MAX_VALUE;
+        try (ZipFile zipFile = builder.get();
+                IOStream<? extends ZipArchiveEntry> entries = zipFile.stream()) {
+            entries.forEach(entry -> {
+                assertNotNull(entry);
+                final String name = entry.getName();
+                assertEquals(expectedLength, name.length(), "Unexpected name length");
+                final String expected = StringUtils.repeat("a", expectedLength);
+                assertEquals(expected, name);
+            });
+        }
+        // Impose a file name length limit and verify that it is enforced.
+        builder.setMaxEntryNameLength(PORTABLE_NAME_LIMIT);
+        final ArchiveException exception = assertThrows(ArchiveException.class, builder::get);
+        final String message = exception.getMessage();
+        assertNotNull(message);
+        assertTrue(message.contains("file name length"));
+        assertTrue(message.contains(String.format("%,d", expectedLength)));
+    }
+
+    @Test
+    void testValid7ZipFile() throws IOException {
+        final SevenZFile.Builder builder = SevenZFile.builder().setURI(getURI("synthetic/long-name/long-name.7z"));
+        final int expectedLength = Short.MAX_VALUE;
+        try (SevenZFile sevenZFile = builder.get()) {
+            final ArchiveEntry entry = sevenZFile.getNextEntry();
+            assertNotNull(entry);
+            final String name = entry.getName();
+            assertEquals(expectedLength, name.length(), "Unexpected name length");
+            final String expected = StringUtils.repeat("a", expectedLength);
+            assertEquals(expected, name);
+        }
+        // SevenZFile parses the whole archive at once, so the builder throws the exception.
+        final ArchiveException exception =
+                assertThrows(ArchiveException.class, () -> builder.setMaxEntryNameLength(PORTABLE_NAME_LIMIT)
+                        .get());
+        final String message = exception.getMessage();
+        assertNotNull(message);
+        assertTrue(message.contains("file name length"));
+        assertTrue(message.contains(String.format("%,d", expectedLength)));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"synthetic/long-name/long-name.dump", "synthetic/long-name/long-name-reversed.dump"})
+    void testValidDumpStreams(final String resourceName) throws IOException {
+        final int rootInode = 2;
+        final int expectedDepth = 127; // number of nested directories
+        final int nameSegmentLength = 255; // length of each segment
+        final int totalEntries = 1 + expectedDepth + 1; // root + 127 dirs + 1 file
+        final int maxInode = rootInode + totalEntries - 1;
+
+        final String nameSegment = StringUtils.repeat('a', nameSegmentLength);
+
+        final DumpArchiveInputStream.Builder builder =
+                DumpArchiveInputStream.builder().setURI(getURI(resourceName));
+
+        try (DumpArchiveInputStream in = builder.get()) {
+            for (int expectedInode = rootInode; expectedInode <= maxInode; expectedInode++) {
+                final boolean isRegularFile = expectedInode == maxInode;
+                final DumpArchiveEntry entry = in.getNextEntry();
+
+                assertNotNull(entry, "Entry " + expectedInode + " should exist");
+
+                // Type checks: root + 127 are directories, last is a regular file.
+                assertEquals(!isRegularFile, entry.isDirectory(), "isDirectory() mismatch");
+
+                final int depth = expectedInode - rootInode; // 0 for root, 1..127 for dirs, 128 for file’s dir count
+
+                final String expectedNameDirs = StringUtils.repeat(nameSegment + "/", depth);
+                final int expectedLength = (nameSegmentLength + 1) * depth - (isRegularFile ? 1 : 0);
+
+                final String actualName = entry.getName();
+
+                assertEquals(expectedInode, entry.getIno(), "inode");
+                assertEquals(expectedLength, actualName.length(), "name length");
+                assertEquals(expectedNameDirs.substring(0, expectedLength), actualName, "full name");
+
+                // Structure checks: every path component is exactly 255×'a'
+
+                String[] parts = actualName.split("/");
+                if (parts.length > 0 && parts[parts.length - 1].isEmpty()) {
+                    // Trailing slash yields an empty final component; ignore it.
+                    parts = Arrays.copyOf(parts, parts.length - 1);
+                }
+
+                // For directories: depth components; for file: depth components (including file itself)
+                assertEquals(depth, parts.length, "component count");
+                for (int i = 0; i < parts.length; i++) {
+                    assertEquals(nameSegmentLength, parts[i].length(), "segment[" + i + "] length");
+                    assertEquals(nameSegment, parts[i], "segment[" + i + "] content");
+                }
+            }
+
+            // Stream should now be exhausted.
+            assertNull(in.getNextEntry(), "No more entries expected after " + totalEntries);
+        }
+
+        try (DumpArchiveInputStream in =
+                builder.setMaxEntryNameLength(PORTABLE_NAME_LIMIT).get()) {
+            int expectedLength;
+            for (int depth = 0; ; depth++) {
+                expectedLength = depth * (nameSegmentLength + 1);
+                if (expectedLength > PORTABLE_NAME_LIMIT) {
+                    break;
+                }
+                assertDoesNotThrow(in::getNextEntry, "Entry " + (rootInode + depth) + " should be readable");
+            }
+            final ArchiveException exception = assertThrows(ArchiveException.class, in::getNextEntry);
+            final String message = exception.getMessage();
+            assertNotNull(message);
+            assertTrue(message.contains("file name length"));
+            assertTrue(message.contains(String.format("%,d", expectedLength)));
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
index 75cacb00f..010042ac9 100644
--- a/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/cpio/CpioArchiveInputStreamTest.java
@@ -29,7 +29,6 @@
 import java.nio.charset.StandardCharsets;
 
 import org.apache.commons.compress.AbstractTest;
-import org.apache.commons.compress.MemoryLimitException;
 import org.apache.commons.compress.archivers.ArchiveException;
 import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.Test;
@@ -90,33 +89,6 @@ void testCpioUnarchiveMultibyteCharName() throws Exception {
         assertEquals(2, count);
     }
 
-    @Test
-    void testEndOfFileInEntry_c_namesize_0x7FFFFFFF() throws Exception {
-        // CPIO header with c_namesize = 0x7FFFFFFF
-        // @formatter:off
-        final String header =
-                "070701" + // c_magic
-                "00000000" + // c_ino
-                "000081A4" + // c_mode
-                "00000000" + // c_uid
-                "00000000" + // c_gid
-                "00000001" + // c_nlink
-                "00000000" + // c_mtime
-                "00000000" + // c_filesize
-                "00000000" + // c_devmajor
-                "00000000" + // c_devminor
-                "00000000" + // c_rdevmajor
-                "00000000" + // c_rdevminor
-                "7FFFFFFF" + // c_namesize
-                "00000000"; // c_check
-        // @formatter:on
-        final byte[] data = new byte[header.getBytes(StandardCharsets.US_ASCII).length + 1];
-        System.arraycopy(header.getBytes(), 0, data, 0, header.getBytes().length);
-        try (CpioArchiveInputStream cpio = CpioArchiveInputStream.builder().setByteArray(data).get()) {
-            assertThrows(MemoryLimitException.class, () -> cpio.getNextEntry());
-        }
-    }
-
     @Test
     void testEndOfFileInEntry_c_namesize_0xFFFFFFFF() throws Exception {
         // CPIO header with c_namesize = 0xFFFFFFFF
diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
index c823b0757..665c29e1e 100644
--- a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStreamTest.java
@@ -36,6 +36,7 @@
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -405,7 +406,8 @@ void testParseTarWithNonNumberPaxHeaders() throws IOException {
     void testParseTarWithSpecialPaxHeaders() throws IOException {
         try (TarArchiveInputStream archive = getTestStream("COMPRESS-530-fail.tar")) {
             assertThrows(ArchiveException.class, () -> archive.getNextEntry());
-            assertThrows(ArchiveException.class, () -> IOUtils.toByteArray(archive));
+            // The PAX header is truncated
+            assertThrows(EOFException.class, () -> IOUtils.toByteArray(archive));
         }
     }
 
@@ -501,7 +503,7 @@ void testShouldReadGNULongNameEntryWithWrongName() throws Exception {
     void testShouldThrowAnExceptionOnTruncatedEntries() throws Exception {
         final Path dir = createTempDirectory("COMPRESS-279");
         try (TarArchiveInputStream is = getTestStream("COMPRESS-279-fail.tar")) {
-            assertThrows(ArchiveException.class, () -> {
+            assertThrows(EOFException.class, () -> {
                 TarArchiveEntry entry = is.getNextTarEntry();
                 int count = 0;
                 while (entry != null) {
@@ -518,7 +520,7 @@ void testShouldThrowAnExceptionOnTruncatedStream() throws Exception {
         final Path dir = createTempDirectory("COMPRESS-279");
         try (TarArchiveInputStream is = getTestStream("COMPRESS-279-fail.tar")) {
             final AtomicInteger count = new AtomicInteger();
-            assertThrows(ArchiveException.class, () -> is.forEach(entry -> Files.copy(is, dir.resolve(String.valueOf(count.getAndIncrement())))));
+            assertThrows(EOFException.class, () -> is.forEach(entry -> Files.copy(is, dir.resolve(String.valueOf(count.getAndIncrement())))));
         }
     }
 
diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java
index 5b8b9a18f..c0f388467 100644
--- a/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarFileTest.java
@@ -26,6 +26,7 @@
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -220,7 +221,7 @@ void testParseTarWithNonNumberPaxHeaders() {
 
     @Test
     void testParseTarWithSpecialPaxHeaders() {
-        assertThrows(ArchiveException.class, () -> new TarFile(getPath("COMPRESS-530-fail.tar")));
+        assertThrows(EOFException.class, () -> new TarFile(getPath("COMPRESS-530-fail.tar")));
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java
index c93755136..126e73531 100644
--- a/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java
@@ -50,6 +50,7 @@
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
 
 class TarUtilsTest extends AbstractTest {
 
@@ -117,12 +118,6 @@ static Stream<Arguments> testReadLongNameHandlesLimits() {
                 Arguments.of("POSIX (padded)", posixLongName, paddedUtf8Bytes(posixLongName)));
     }
 
-    static Stream<Arguments> testReadLongNameThrowsOnTruncation() {
-        return Stream.of(
-                Arguments.of(Integer.MAX_VALUE, "truncated long name"),
-                Arguments.of(Long.MAX_VALUE, "invalid long name"));
-    }
-
     private static byte[] utf8Bytes(final String s) {
         return s.getBytes(UTF_8);
     }
@@ -413,7 +408,7 @@ void testReadLongNameHandlesLimits(final String kind, final String expectedName,
         Arrays.fill(dataWithGarbage, data.length, dataWithGarbage.length, (byte) 0xFF);
 
         try (InputStream in = new ByteArrayInputStream(dataWithGarbage)) {
-            final String actualName = TarUtils.readLongName(in, ZipEncodingHelper.getZipEncoding(UTF_8), entry);
+            final String actualName = TarUtils.readLongName(in, ZipEncodingHelper.getZipEncoding(UTF_8), Integer.MAX_VALUE, entry);
             assertEquals(
                     expectedName,
                     actualName,
@@ -422,20 +417,17 @@ void testReadLongNameHandlesLimits(final String kind, final String expectedName,
     }
 
     @ParameterizedTest(name = "readLongName of {0} bytes throws ArchiveException")
-    @MethodSource
-    void testReadLongNameThrowsOnTruncation(final long size, final CharSequence expectedMessage) throws IOException {
+    @ValueSource(longs = { Integer.MAX_VALUE, Long.MAX_VALUE })
+    void testReadLongNameThrowsOnTruncation(final long size) throws IOException {
         final TarArchiveEntry entry = new TarArchiveEntry("test");
         entry.setSize(size); // absurdly large so any finite stream truncates
         try (InputStream in = new NullInputStream()) {
-            final ArchiveException ex = assertThrows(
-                    ArchiveException.class,
-                    () -> TarUtils.readLongName(in, TarUtils.DEFAULT_ENCODING, entry),
-                    "Expected ArchiveException due to truncated long name, but no exception was thrown");
+            final IOException ex = assertThrows(
+                    IOException.class,
+                    () -> TarUtils.readLongName(in, TarUtils.DEFAULT_ENCODING, Integer.MAX_VALUE, entry),
+                    "Expected IOException due to out of range size for Java byte arrays");
             final String actualMessage = StringUtils.toRootLowerCase(ex.getMessage());
             assertNotNull(actualMessage, "Exception message should not be null");
-            assertTrue(
-                    actualMessage.contains(expectedMessage),
-                    () -> "Expected exception message to contain '" + expectedMessage + "', but got: " + actualMessage);
             assertTrue(
                     actualMessage.contains(String.format("%,d", size)),
                     () -> "Expected exception message to mention '" + size + "', but got: " + actualMessage);
diff --git a/src/test/resources/synthetic/long-name/bin-big-endian.cpio b/src/test/resources/synthetic/long-name/bin-big-endian.cpio
new file mode 100644
index 000000000..322464e49
Binary files /dev/null and b/src/test/resources/synthetic/long-name/bin-big-endian.cpio differ
diff --git a/src/test/resources/synthetic/long-name/bin-little-endian.cpio b/src/test/resources/synthetic/long-name/bin-little-endian.cpio
new file mode 100644
index 000000000..91ae61d59
Binary files /dev/null and b/src/test/resources/synthetic/long-name/bin-little-endian.cpio differ
diff --git a/src/test/resources/synthetic/long-name/bsd-fail.ar b/src/test/resources/synthetic/long-name/bsd-fail.ar
new file mode 100644
index 000000000..f228574bc
Binary files /dev/null and b/src/test/resources/synthetic/long-name/bsd-fail.ar differ
diff --git a/src/test/resources/synthetic/long-name/bsd-short-max-value.ar b/src/test/resources/synthetic/long-name/bsd-short-max-value.ar
new file mode 100644
index 000000000..45061b3fb
Binary files /dev/null and b/src/test/resources/synthetic/long-name/bsd-short-max-value.ar differ
diff --git a/src/test/resources/synthetic/long-name/crc-fail.cpio b/src/test/resources/synthetic/long-name/crc-fail.cpio
new file mode 100644
index 000000000..f9563f872
Binary files /dev/null and b/src/test/resources/synthetic/long-name/crc-fail.cpio differ
diff --git a/src/test/resources/synthetic/long-name/crc.cpio b/src/test/resources/synthetic/long-name/crc.cpio
new file mode 100644
index 000000000..c4a35ef8a
--- /dev/null
+++ b/src/test/resources/synthetic/long-name/crc.cpio
@@ -0,0 +1 @@
+07070200000000000081a40000000000000000000000010000000000000000000000000000000000000000000000000000800000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
\ No newline at end of file
diff --git a/src/test/resources/synthetic/long-name/gnu-fail.ar b/src/test/resources/synthetic/long-name/gnu-fail.ar
new file mode 100644
index 000000000..623945a11
Binary files /dev/null and b/src/test/resources/synthetic/long-name/gnu-fail.ar differ
diff --git a/src/test/resources/synthetic/long-name/gnu-fail.tar b/src/test/resources/synthetic/long-name/gnu-fail.tar
new file mode 100644
index 000000000..76dc9923a
Binary files /dev/null and b/src/test/resources/synthetic/long-name/gnu-fail.tar differ
diff --git a/src/test/resources/synthetic/long-name/gnu-short-max-value.ar b/src/test/resources/synthetic/long-name/gnu-short-max-value.ar
new file mode 100644
index 000000000..4746343e4
Binary files /dev/null and b/src/test/resources/synthetic/long-name/gnu-short-max-value.ar differ
diff --git a/src/test/resources/synthetic/long-name/gnu.tar b/src/test/resources/synthetic/long-name/gnu.tar
new file mode 100644
index 000000000..f789171a9
Binary files /dev/null and b/src/test/resources/synthetic/long-name/gnu.tar differ
diff --git a/src/test/resources/synthetic/long-name/long-name-reversed.dump b/src/test/resources/synthetic/long-name/long-name-reversed.dump
new file mode 100644
index 000000000..be8e2b3aa
Binary files /dev/null and b/src/test/resources/synthetic/long-name/long-name-reversed.dump differ
diff --git a/src/test/resources/synthetic/long-name/long-name.7z b/src/test/resources/synthetic/long-name/long-name.7z
new file mode 100644
index 000000000..8a88f1f4d
Binary files /dev/null and b/src/test/resources/synthetic/long-name/long-name.7z differ
diff --git a/src/test/resources/synthetic/long-name/long-name.arj b/src/test/resources/synthetic/long-name/long-name.arj
new file mode 100644
index 000000000..27d3cec2c
Binary files /dev/null and b/src/test/resources/synthetic/long-name/long-name.arj differ
diff --git a/src/test/resources/synthetic/long-name/long-name.dump b/src/test/resources/synthetic/long-name/long-name.dump
new file mode 100644
index 000000000..561a47fae
Binary files /dev/null and b/src/test/resources/synthetic/long-name/long-name.dump differ
diff --git a/src/test/resources/synthetic/long-name/long-name.zip b/src/test/resources/synthetic/long-name/long-name.zip
new file mode 100644
index 000000000..64ddc64c5
Binary files /dev/null and b/src/test/resources/synthetic/long-name/long-name.zip differ
diff --git a/src/test/resources/synthetic/long-name/newc-fail.cpio b/src/test/resources/synthetic/long-name/newc-fail.cpio
new file mode 100644
index 000000000..feedcbd78
Binary files /dev/null and b/src/test/resources/synthetic/long-name/newc-fail.cpio differ
diff --git a/src/test/resources/synthetic/long-name/newc.cpio b/src/test/resources/synthetic/long-name/newc.cpio
new file mode 100644
index 000000000..e7b0f8cb4
--- /dev/null
+++ b/src/test/resources/synthetic/long-name/newc.cpio
@@ -0,0 +1 @@
+07070100000000000081a40000000000000000000000010000000000000000000000000000000000000000000000000000800000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
\ No newline at end of file
diff --git a/src/test/resources/synthetic/long-name/odc-fail.cpio b/src/test/resources/synthetic/long-name/odc-fail.cpio
new file mode 100644
index 000000000..4038c1e90
Binary files /dev/null and b/src/test/resources/synthetic/long-name/odc-fail.cpio differ
diff --git a/src/test/resources/synthetic/long-name/odc.cpio b/src/test/resources/synthetic/long-name/odc.cpio
new file mode 100644
index 000000000..d2a72984f
--- /dev/null
+++ b/src/test/resources/synthetic/long-name/odc.cpio
@@ -0,0 +1 @@
+0707070000000000001006440000000000000000010000000000000000010000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [...]
\ No newline at end of file
diff --git a/src/test/resources/synthetic/long-name/pax-fail.tar b/src/test/resources/synthetic/long-name/pax-fail.tar
new file mode 100644
index 000000000..296c37056
Binary files /dev/null and b/src/test/resources/synthetic/long-name/pax-fail.tar differ
diff --git a/src/test/resources/synthetic/long-name/pax.tar b/src/test/resources/synthetic/long-name/pax.tar
new file mode 100644
index 000000000..2af1df4ca
Binary files /dev/null and b/src/test/resources/synthetic/long-name/pax.tar differ