diff --git a/pom.xml b/pom.xml
index fdf17217740..5b62a50c2eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,6 +92,17 @@ jar, tar, zip, dump, 7z, arj.
${powermock.version}
test
+
+ org.slf4j
+ slf4j-api
+ 1.7.25
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.25
+ test
+
diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java
index 79c7bca86de..5ae2534c004 100644
--- a/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java
@@ -33,6 +33,8 @@
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.CharsetNames;
import org.apache.commons.compress.utils.CountingOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* The TarOutputStream writes a UNIX tar archive as an OutputStream.
@@ -41,6 +43,9 @@
* @NotThreadSafe
*/
public class TarArchiveOutputStream extends ArchiveOutputStream {
+
+ private static Logger logger = LoggerFactory.getLogger(TarArchiveOutputStream.class);
+
/** Fail if a long file name is required in the archive. */
public static final int LONGFILE_ERROR = 0;
@@ -61,18 +66,18 @@ public class TarArchiveOutputStream extends ArchiveOutputStream {
/** POSIX/PAX extensions are used to store big numbers in the archive. */
public static final int BIGNUMBER_POSIX = 2;
-
- private long currSize;
- private String currName;
- private long currBytes;
- private final byte[] recordBuf;
- private int assemLen;
- private final byte[] assemBuf;
- private int longFileMode = LONGFILE_ERROR;
- private int bigNumberMode = BIGNUMBER_ERROR;
+ private static final int RECORD_SIZE = 512;
+
+ private long currSize;
+ private String currName;
+ private long currBytes;
+ private final byte[] recordBuf;
+ private int assemLen;
+ private final byte[] assemBuf;
+ private int longFileMode = LONGFILE_ERROR;
+ private int bigNumberMode = BIGNUMBER_ERROR;
private int recordsWritten;
private final int recordsPerBlock;
- private final int recordSize;
private boolean closed = false;
@@ -93,12 +98,15 @@ public class TarArchiveOutputStream extends ArchiveOutputStream {
private static final ZipEncoding ASCII =
ZipEncodingHelper.getZipEncoding("ASCII");
+ private static int BLOCK_SIZE_UNSPECIFIED = -511;
+
+ private int recordSize = RECORD_SIZE;
/**
* Constructor for TarInputStream.
* @param os the output stream to use
*/
public TarArchiveOutputStream(final OutputStream os) {
- this(os, TarConstants.DEFAULT_BLKSIZE, TarConstants.DEFAULT_RCDSIZE);
+ this(os, BLOCK_SIZE_UNSPECIFIED);
}
/**
@@ -108,50 +116,86 @@ public TarArchiveOutputStream(final OutputStream os) {
* @since 1.4
*/
public TarArchiveOutputStream(final OutputStream os, final String encoding) {
- this(os, TarConstants.DEFAULT_BLKSIZE, TarConstants.DEFAULT_RCDSIZE, encoding);
+ this(os, BLOCK_SIZE_UNSPECIFIED, encoding);
}
/**
* Constructor for TarInputStream.
* @param os the output stream to use
- * @param blockSize the block size to use
+ * @param blockSize the block size to use. Must be a multiple of 512 bytes.
*/
public TarArchiveOutputStream(final OutputStream os, final int blockSize) {
- this(os, blockSize, TarConstants.DEFAULT_RCDSIZE);
+ this(os, blockSize, null);
}
+
/**
* Constructor for TarInputStream.
* @param os the output stream to use
* @param blockSize the block size to use
- * @param encoding name of the encoding to use for file names
- * @since 1.4
+ * @param recordSize the record size to use. Must be 512 bytes.
+ * @deprecated recordSize must always be 512 bytes. An IllegalArgumentException will be thrown
+ * if any other value is used
*/
+ @Deprecated
public TarArchiveOutputStream(final OutputStream os, final int blockSize,
- final String encoding) {
- this(os, blockSize, TarConstants.DEFAULT_RCDSIZE, encoding);
+ final int recordSize) {
+ this(os, blockSize, recordSize, null);
}
/**
* Constructor for TarInputStream.
* @param os the output stream to use
- * @param blockSize the block size to use
- * @param recordSize the record size to use
+ * @param blockSize the block size to use . Must be a multiple of 512 bytes.
+ * @param recordSize the record size to use. Must be 512 bytes.
+ * @param encoding name of the encoding to use for file names
+ * @since 1.4
+ * @deprecated recordSize must always be 512 bytes. An IllegalArgumentException will be thrown
+ * if any other value is used.
*/
- public TarArchiveOutputStream(final OutputStream os, final int blockSize, final int recordSize) {
- this(os, blockSize, recordSize, null);
+ @Deprecated
+ public TarArchiveOutputStream(final OutputStream os, final int blockSize,
+ final int recordSize, final String encoding) {
+ this(os, blockSize, encoding,recordSize);
}
/**
* Constructor for TarInputStream.
+ *
* @param os the output stream to use
- * @param blockSize the block size to use
- * @param recordSize the record size to use
+ * @param blockSize the block size to use. Must be a multiple of 512 bytes.
* @param encoding name of the encoding to use for file names
* @since 1.4
*/
public TarArchiveOutputStream(final OutputStream os, final int blockSize,
- final int recordSize, final String encoding) {
+ final String encoding) {
+ this(os, blockSize, encoding, RECORD_SIZE);
+ }
+
+ private TarArchiveOutputStream(final OutputStream os, final int blockSize,
+ final String encoding, int recordSize) {
+ if (recordSize != RECORD_SIZE) {
+ logger.warn("{}", String.format(
+ "An invalid record size of %,d bytes has been requested. Tar records should always be "
+ + "512 bytes in length. The invalid size will be used - however the tar file may not be "
+ + " usable unless the invalid record size is known and the extracting tool or library supports "
+ + "setting invalid record sizes.",
+ recordSize));
+ this.recordSize = recordSize;
+ }
+
+ int realBlockSize;
+ if (BLOCK_SIZE_UNSPECIFIED == blockSize) {
+ realBlockSize = recordSize;
+ } else {
+ realBlockSize = blockSize;
+ }
+
+ if (realBlockSize <= 0 || realBlockSize % recordSize != 0) {
+ throw new IllegalArgumentException(
+ "Block size must be a multiple of record size. Attempt to set size of "
+ + blockSize);
+ }
out = new CountingOutputStream(os);
this.encoding = encoding;
this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
@@ -159,8 +203,7 @@ public TarArchiveOutputStream(final OutputStream os, final int blockSize,
this.assemLen = 0;
this.assemBuf = new byte[recordSize];
this.recordBuf = new byte[recordSize];
- this.recordSize = recordSize;
- this.recordsPerBlock = blockSize / recordSize;
+ this.recordsPerBlock = realBlockSize / recordSize;
}
/**
@@ -208,11 +251,11 @@ public long getBytesWritten() {
/**
* Ends the TAR archive without closing the underlying OutputStream.
- *
+ *
* An archive consists of a series of file entries terminated by an
- * end-of-archive entry, which consists of two 512 blocks of zero bytes.
+ * end-of-archive entry, which consists of two 512 blocks of zero bytes.
* POSIX.1 requires two EOF records, like some other implementations.
- *
+ *
* @throws IOException on error
*/
@Override
@@ -251,9 +294,11 @@ public void close() throws IOException {
* Get the record size being used by this stream's TarBuffer.
*
* @return The TarBuffer record size.
+ * @deprecated
*/
+ @Deprecated
public int getRecordSize() {
- return this.recordSize;
+ return recordSize;
}
/**
@@ -278,12 +323,12 @@ public void putArchiveEntry(final ArchiveEntry archiveEntry) throws IOException
final Map paxHeaders = new HashMap<>();
final String entryName = entry.getName();
final boolean paxHeaderContainsPath = handleLongName(entry, entryName, paxHeaders, "path",
- TarConstants.LF_GNUTYPE_LONGNAME, "file name");
+ TarConstants.LF_GNUTYPE_LONGNAME, "file name");
final String linkName = entry.getLinkName();
final boolean paxHeaderContainsLinkPath = linkName != null && linkName.length() > 0
&& handleLongName(entry, linkName, paxHeaders, "linkpath",
- TarConstants.LF_GNUTYPE_LONGLINK, "link name");
+ TarConstants.LF_GNUTYPE_LONGLINK, "link name");
if (bigNumberMode == BIGNUMBER_POSIX) {
addPaxHeadersForBigNumbers(paxHeaders, entry);
@@ -307,7 +352,7 @@ && handleLongName(entry, linkName, paxHeaders, "linkpath",
}
entry.writeEntryHeader(recordBuf, zipEncoding,
- bigNumberMode == BIGNUMBER_STAR);
+ bigNumberMode == BIGNUMBER_STAR);
writeRecord(recordBuf);
currBytes = 0;
@@ -336,7 +381,7 @@ public void closeArchiveEntry() throws IOException {
if (finished) {
throw new IOException("Stream has already been finished");
}
- if (!haveUnclosedEntry){
+ if (!haveUnclosedEntry) {
throw new IOException("No current entry to close");
}
if (assemLen > 0) {
@@ -352,9 +397,9 @@ public void closeArchiveEntry() throws IOException {
if (currBytes < currSize) {
throw new IOException("entry '" + currName + "' closed at '"
- + currBytes
- + "' before the '" + currSize
- + "' bytes specified in the header were written");
+ + currBytes
+ + "' before the '" + currSize
+ + "' bytes specified in the header were written");
}
haveUnclosedEntry = false;
}
@@ -380,9 +425,9 @@ public void write(final byte[] wBuf, int wOffset, int numToWrite) throws IOExcep
}
if (currBytes + numToWrite > currSize) {
throw new IOException("request to write '" + numToWrite
- + "' bytes exceeds size in header of '"
- + currSize + "' bytes for entry '"
- + currName + "'");
+ + "' bytes exceeds size in header of '"
+ + currSize + "' bytes for entry '"
+ + currName + "'");
//
// We have to deal with assembly!!!
@@ -398,9 +443,9 @@ public void write(final byte[] wBuf, int wOffset, int numToWrite) throws IOExcep
final int aLen = recordBuf.length - assemLen;
System.arraycopy(assemBuf, 0, recordBuf, 0,
- assemLen);
+ assemLen);
System.arraycopy(wBuf, wOffset, recordBuf,
- assemLen, aLen);
+ assemLen, aLen);
writeRecord(recordBuf);
currBytes += recordBuf.length;
@@ -409,7 +454,7 @@ public void write(final byte[] wBuf, int wOffset, int numToWrite) throws IOExcep
assemLen = 0;
} else {
System.arraycopy(wBuf, wOffset, assemBuf, assemLen,
- numToWrite);
+ numToWrite);
wOffset += numToWrite;
assemLen += numToWrite;
@@ -425,7 +470,7 @@ public void write(final byte[] wBuf, int wOffset, int numToWrite) throws IOExcep
while (numToWrite > 0) {
if (numToWrite < recordBuf.length) {
System.arraycopy(wBuf, wOffset, assemBuf, assemLen,
- numToWrite);
+ numToWrite);
assemLen += numToWrite;
@@ -447,14 +492,14 @@ public void write(final byte[] wBuf, int wOffset, int numToWrite) throws IOExcep
* @since 1.4
*/
void writePaxHeaders(final TarArchiveEntry entry,
- final String entryName,
- final Map headers) throws IOException {
+ final String entryName,
+ final Map headers) throws IOException {
String name = "./PaxHeaders.X/" + stripTo7Bits(entryName);
if (name.length() >= TarConstants.NAMELEN) {
name = name.substring(0, TarConstants.NAMELEN - 1);
}
final TarArchiveEntry pex = new TarArchiveEntry(name,
- TarConstants.LF_PAX_EXTENDED_HEADER_LC);
+ TarConstants.LF_PAX_EXTENDED_HEADER_LC);
transferModTime(entry, pex);
final StringWriter w = new StringWriter();
@@ -500,8 +545,8 @@ private String stripTo7Bits(final String name) {
}
/**
- * @return true if the character could lead to problems when used
- * inside a TarArchiveEntry name for a PAX header.
+ * @return true if the character could lead to problems when used inside a TarArchiveEntry name
+ * for a PAX header.
*/
private boolean shouldBeReplaced(final char c) {
return c == 0 // would be read as Trailing null
@@ -510,8 +555,8 @@ private boolean shouldBeReplaced(final char c) {
}
/**
- * Write an EOF (end of archive) record to the tar archive.
- * An EOF record consists of a record of all zeros.
+ * Write an EOF (end of archive) record to the tar archive. An EOF record consists of a record
+ * of all zeros.
*/
private void writeEOFRecord() throws IOException {
Arrays.fill(recordBuf, (byte) 0);
@@ -525,13 +570,13 @@ public void flush() throws IOException {
@Override
public ArchiveEntry createArchiveEntry(final File inputFile, final String entryName)
- throws IOException {
- if(finished) {
+ throws IOException {
+ if (finished) {
throw new IOException("Stream has already been finished");
}
return new TarArchiveEntry(inputFile, entryName);
}
-
+
/**
* Write an archive record to the archive.
*
@@ -541,15 +586,15 @@ public ArchiveEntry createArchiveEntry(final File inputFile, final String entryN
private void writeRecord(final byte[] record) throws IOException {
if (record.length != recordSize) {
throw new IOException("record to write has length '"
- + record.length
- + "' which is not the record size of '"
- + recordSize + "'");
+ + record.length
+ + "' which is not the record size of '"
+ + recordSize + "'");
}
out.write(record);
recordsWritten++;
}
-
+
/**
* Write an archive record to the archive, where the record may be
* inside of a larger array buffer. The buffer must be "offset plus
@@ -560,12 +605,12 @@ private void writeRecord(final byte[] record) throws IOException {
* @throws IOException on error
*/
private void writeRecord(final byte[] buf, final int offset) throws IOException {
-
+
if (offset + recordSize > buf.length) {
throw new IOException("record has length '" + buf.length
- + "' with offset '" + offset
- + "' which is less than the record size of '"
- + recordSize + "'");
+ + "' with offset '" + offset
+ + "' which is less than the record size of '"
+ + recordSize + "'");
}
out.write(buf, offset, recordSize);
@@ -582,28 +627,28 @@ private void padAsNeeded() throws IOException {
}
private void addPaxHeadersForBigNumbers(final Map paxHeaders,
- final TarArchiveEntry entry) {
+ final TarArchiveEntry entry) {
addPaxHeaderForBigNumber(paxHeaders, "size", entry.getSize(),
- TarConstants.MAXSIZE);
+ TarConstants.MAXSIZE);
addPaxHeaderForBigNumber(paxHeaders, "gid", entry.getLongGroupId(),
- TarConstants.MAXID);
+ TarConstants.MAXID);
addPaxHeaderForBigNumber(paxHeaders, "mtime",
- entry.getModTime().getTime() / 1000,
- TarConstants.MAXSIZE);
+ entry.getModTime().getTime() / 1000,
+ TarConstants.MAXSIZE);
addPaxHeaderForBigNumber(paxHeaders, "uid", entry.getLongUserId(),
- TarConstants.MAXID);
+ TarConstants.MAXID);
// star extensions by J\u00f6rg Schilling
addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devmajor",
- entry.getDevMajor(), TarConstants.MAXID);
+ entry.getDevMajor(), TarConstants.MAXID);
addPaxHeaderForBigNumber(paxHeaders, "SCHILY.devminor",
- entry.getDevMinor(), TarConstants.MAXID);
+ entry.getDevMinor(), TarConstants.MAXID);
// there is no PAX header for file mode
failForBigNumber("mode", entry.getMode(), TarConstants.MAXID);
}
private void addPaxHeaderForBigNumber(final Map paxHeaders,
- final String header, final long value,
- final long maxValue) {
+ final String header, final long value,
+ final long maxValue) {
if (value < 0 || value > maxValue) {
paxHeaders.put(header, String.valueOf(value));
}
@@ -613,29 +658,32 @@ private void failForBigNumbers(final TarArchiveEntry entry) {
failForBigNumber("entry size", entry.getSize(), TarConstants.MAXSIZE);
failForBigNumberWithPosixMessage("group id", entry.getLongGroupId(), TarConstants.MAXID);
failForBigNumber("last modification time",
- entry.getModTime().getTime() / 1000,
- TarConstants.MAXSIZE);
+ entry.getModTime().getTime() / 1000,
+ TarConstants.MAXSIZE);
failForBigNumber("user id", entry.getLongUserId(), TarConstants.MAXID);
failForBigNumber("mode", entry.getMode(), TarConstants.MAXID);
failForBigNumber("major device number", entry.getDevMajor(),
- TarConstants.MAXID);
+ TarConstants.MAXID);
failForBigNumber("minor device number", entry.getDevMinor(),
- TarConstants.MAXID);
+ TarConstants.MAXID);
}
private void failForBigNumber(final String field, final long value, final long maxValue) {
failForBigNumber(field, value, maxValue, "");
}
- private void failForBigNumberWithPosixMessage(final String field, final long value, final long maxValue) {
- failForBigNumber(field, value, maxValue, " Use STAR or POSIX extensions to overcome this limit");
+ private void failForBigNumberWithPosixMessage(final String field, final long value,
+ final long maxValue) {
+ failForBigNumber(field, value, maxValue,
+ " Use STAR or POSIX extensions to overcome this limit");
}
- private void failForBigNumber(final String field, final long value, final long maxValue, final String additionalMsg) {
+ private void failForBigNumber(final String field, final long value, final long maxValue,
+ final String additionalMsg) {
if (value < 0 || value > maxValue) {
throw new RuntimeException(field + " '" + value //NOSONAR
- + "' is too big ( > "
- + maxValue + " )." + additionalMsg);
+ + "' is too big ( > "
+ + maxValue + " )." + additionalMsg);
}
}
@@ -661,9 +709,9 @@ private void failForBigNumber(final String field, final long value, final long m
* @param fieldName the name of the field
* @return whether a pax header has been written.
*/
- private boolean handleLongName(final TarArchiveEntry entry , final String name,
- final Map paxHeaders,
- final String paxHeaderName, final byte linkType, final String fieldName)
+ private boolean handleLongName(final TarArchiveEntry entry, final String name,
+ final Map paxHeaders,
+ final String paxHeaderName, final byte linkType, final String fieldName)
throws IOException {
final ByteBuffer encodedName = zipEncoding.encode(name);
final int len = encodedName.limit() - encodedName.position();
@@ -675,7 +723,8 @@ private boolean handleLongName(final TarArchiveEntry entry , final String name,
} else if (longFileMode == LONGFILE_GNU) {
// create a TarEntry for the LongLink, the contents
// of which are the link's name
- final TarArchiveEntry longLinkEntry = new TarArchiveEntry(TarConstants.GNU_LONGLINK, linkType);
+ final TarArchiveEntry longLinkEntry = new TarArchiveEntry(TarConstants.GNU_LONGLINK,
+ linkType);
longLinkEntry.setSize(len + 1l); // +1 for NUL
transferModTime(entry, longLinkEntry);
@@ -685,8 +734,8 @@ private boolean handleLongName(final TarArchiveEntry entry , final String name,
closeArchiveEntry();
} else if (longFileMode != LONGFILE_TRUNCATE) {
throw new RuntimeException(fieldName + " '" + name //NOSONAR
- + "' is too long ( > "
- + TarConstants.NAMELEN + " bytes)");
+ + "' is too long ( > "
+ + TarConstants.NAMELEN + " bytes)");
}
}
return false;
diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java
index 85cdb66a30f..9c9c2ad1f5c 100644
--- a/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java
@@ -26,6 +26,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.security.MessageDigest;
import java.util.Calendar;
import java.util.Date;
@@ -100,10 +101,10 @@ public void testBigNumberStarMode() throws Exception {
tos.write(new byte[10 * 1024]);
final byte[] data = bos.toByteArray();
assertEquals(0x80,
- data[TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN] & 0x80);
+ data[TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN] & 0x80);
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -126,12 +127,12 @@ public void testBigNumberPosixMode() throws Exception {
tos.write(new byte[10 * 1024]);
final byte[] data = bos.toByteArray();
assertEquals("00000000000 ",
- new String(data,
- 1024 + TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN, 12,
- CharsetNames.UTF_8));
+ new String(data,
+ 1024 + TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN, 12,
+ CharsetNames.UTF_8));
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -148,11 +149,11 @@ public void testWriteSimplePaxHeaders() throws Exception {
m.put("a", "b");
final byte[] data = writePaxHeader(m);
assertEquals("00000000006 ",
- new String(data, TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN, 12,
- CharsetNames.UTF_8));
+ new String(data, TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN, 12,
+ CharsetNames.UTF_8));
assertEquals("6 a=b\n", new String(data, 512, 6, CharsetNames.UTF_8));
}
@@ -160,38 +161,38 @@ public void testWriteSimplePaxHeaders() throws Exception {
public void testPaxHeadersWithLength99() throws Exception {
final Map m = new HashMap<>();
m.put("a",
- "0123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789"
- + "012");
+ "0123456789012345678901234567890123456789"
+ + "01234567890123456789012345678901234567890123456789"
+ + "012");
final byte[] data = writePaxHeader(m);
assertEquals("00000000143 ",
- new String(data, TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN, 12,
- CharsetNames.UTF_8));
+ new String(data, TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN, 12,
+ CharsetNames.UTF_8));
assertEquals("99 a=0123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789"
- + "012\n", new String(data, 512, 99, CharsetNames.UTF_8));
+ + "01234567890123456789012345678901234567890123456789"
+ + "012\n", new String(data, 512, 99, CharsetNames.UTF_8));
}
@Test
public void testPaxHeadersWithLength101() throws Exception {
final Map m = new HashMap<>();
m.put("a",
- "0123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789"
- + "0123");
+ "0123456789012345678901234567890123456789"
+ + "01234567890123456789012345678901234567890123456789"
+ + "0123");
final byte[] data = writePaxHeader(m);
assertEquals("00000000145 ",
- new String(data, TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN, 12,
- CharsetNames.UTF_8));
+ new String(data, TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN, 12,
+ CharsetNames.UTF_8));
assertEquals("101 a=0123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789"
- + "0123\n", new String(data, 512, 101, CharsetNames.UTF_8));
+ + "01234567890123456789012345678901234567890123456789"
+ + "0123\n", new String(data, 512, 101, CharsetNames.UTF_8));
}
private byte[] writePaxHeader(final Map m) throws Exception {
@@ -226,7 +227,7 @@ public void testWriteLongFileNamePosixMode() throws Exception {
tos.closeArchiveEntry();
final byte[] data = bos.toByteArray();
assertEquals("160 path=" + n + "\n",
- new String(data, 512, 160, CharsetNames.UTF_8));
+ new String(data, 512, 160, CharsetNames.UTF_8));
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -248,11 +249,11 @@ public void testOldEntryStarMode() throws Exception {
tos.write(new byte[10 * 1024]);
final byte[] data = bos.toByteArray();
assertEquals((byte) 0xff,
- data[TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN
- + TarConstants.SIZELEN]);
+ data[TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN
+ + TarConstants.SIZELEN]);
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -279,13 +280,13 @@ public void testOldEntryPosixMode() throws Exception {
tos.write(new byte[10 * 1024]);
final byte[] data = bos.toByteArray();
assertEquals("00000000000 ",
- new String(data,
- 1024 + TarConstants.NAMELEN
- + TarConstants.MODELEN
- + TarConstants.UIDLEN
- + TarConstants.GIDLEN
- + TarConstants.SIZELEN, 12,
- CharsetNames.UTF_8));
+ new String(data,
+ 1024 + TarConstants.NAMELEN
+ + TarConstants.MODELEN
+ + TarConstants.UIDLEN
+ + TarConstants.GIDLEN
+ + TarConstants.SIZELEN, 12,
+ CharsetNames.UTF_8));
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -328,7 +329,7 @@ public void testWriteNonAsciiPathNamePaxHeader() throws Exception {
tos.close();
final byte[] data = bos.toByteArray();
assertEquals("11 path=" + n + "\n",
- new String(data, 512, 11, CharsetNames.UTF_8));
+ new String(data, 512, 11, CharsetNames.UTF_8));
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -351,7 +352,7 @@ public void testWriteNonAsciiLinkPathNamePaxHeader() throws Exception {
tos.close();
final byte[] data = bos.toByteArray();
assertEquals("15 linkpath=" + n + "\n",
- new String(data, 512, 15, CharsetNames.UTF_8));
+ new String(data, 512, 15, CharsetNames.UTF_8));
final TarArchiveInputStream tin =
new TarArchiveInputStream(new ByteArrayInputStream(data));
final TarArchiveEntry e = tin.getNextTarEntry();
@@ -399,8 +400,8 @@ private void testRoundtripWith67CharFileName(final int mode) throws Exception {
@Test
public void testWriteLongDirectoryNameErrorMode() throws Exception {
final String n = "01234567890123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789/";
+ + "01234567890123456789012345678901234567890123456789"
+ + "01234567890123456789012345678901234567890123456789/";
try {
final TarArchiveEntry t = new TarArchiveEntry(n);
@@ -524,8 +525,8 @@ public void testWriteNonAsciiNameWithUnfortunateNamePosixMode() throws Exception
@Test
public void testWriteLongLinkNameErrorMode() throws Exception {
final String linkname = "01234567890123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789"
- + "01234567890123456789012345678901234567890123456789/test";
+ + "01234567890123456789012345678901234567890123456789"
+ + "01234567890123456789012345678901234567890123456789/test";
final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
entry.setLinkName(linkname);
@@ -548,7 +549,7 @@ public void testWriteLongLinkNameTruncateMode() throws Exception {
final String linkname = "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789"
+ "01234567890123456789012345678901234567890123456789/";
- final TarArchiveEntry entry = new TarArchiveEntry("test" , TarConstants.LF_SYMLINK);
+ final TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
entry.setLinkName(linkname);
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -607,31 +608,68 @@ private void testWriteLongLinkName(final int mode) throws Exception {
tin.close();
}
+
@Test
- public void testPadsOutputToFullBlockLength() throws Exception {
+ public void testBlockSizes() throws Exception {
+ String fileName = "/test1.xml";
+ byte[] contents = getResourceContents(fileName);
+ testPadding(TarConstants.DEFAULT_BLKSIZE, fileName, contents); // USTAR / pre-pax
+ testPadding(5120, fileName, contents); // PAX default
+ testPadding(1<<15, fileName, contents); //PAX max
+ testPadding(-2, fileName, contents); // don't specify a block size -> use minimum length
+ try {
+ testPadding(511, fileName, contents); // don't specify a block size -> use minimum length
+ fail("should have thrown an illegal argument exception");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+ try {
+ testPadding(0, fileName, contents); // don't specify a block size -> use minimum length
+ fail("should have thrown an illegal argument exception");
+ } catch (IllegalArgumentException e) {
+ //expected
+ }
+ }
+
+ private void testPadding(int blockSize, String fileName, byte[] contents) throws IOException {
final File f = File.createTempFile("commons-compress-padding", ".tar");
f.deleteOnExit();
final FileOutputStream fos = new FileOutputStream(f);
- final TarArchiveOutputStream tos = new TarArchiveOutputStream(fos);
- final File file1 = getFile("test1.xml");
- final TarArchiveEntry sEntry = new TarArchiveEntry(file1, file1.getName());
+ final TarArchiveOutputStream tos;
+ if(blockSize != -2) {
+ tos = new TarArchiveOutputStream(fos, blockSize);
+ } else {
+ blockSize = 512;
+ tos = new TarArchiveOutputStream(fos);
+ }
+ TarArchiveEntry sEntry;
+ sEntry = new TarArchiveEntry(fileName);
+ sEntry.setSize(contents.length);
tos.putArchiveEntry(sEntry);
- final FileInputStream in = new FileInputStream(file1);
- IOUtils.copy(in, tos);
- in.close();
+ tos.write(contents);
tos.closeArchiveEntry();
tos.close();
- // test1.xml is small enough to fit into the default block size
- assertEquals(TarConstants.DEFAULT_BLKSIZE, f.length());
+ int fileRecordsSize = (int)Math.ceil((double) contents.length / 512)*512;
+ final int headerSize = 512;
+ final int endOfArchiveSize = 1024;
+ int unpaddedSize = headerSize + fileRecordsSize + endOfArchiveSize;
+ int paddedSize = (int)Math.ceil((double)unpaddedSize/blockSize)*blockSize;
+ assertEquals(paddedSize, f.length());
+ }
+
+ private byte[] getResourceContents(String name) throws IOException {
+ ByteArrayOutputStream bos;
+ try (InputStream resourceAsStream = getClass().getResourceAsStream(name)) {
+ bos = new ByteArrayOutputStream();
+ IOUtils.copy(resourceAsStream, bos);
+ }
+ return bos.toByteArray();
}
/**
- * When using long file names the longLinkEntry included the
- * current timestamp as the Entry modification date. This was
- * never exposed to the client but it caused identical archives to
+ * When using long file names the longLinkEntry included the current timestamp as the Entry
+ * modification date. This was never exposed to the client but it caused identical archives to
* have different MD5 hashes.
- *
- * @throws Exception
*/
@Test
public void testLongNameMd5Hash() throws Exception {
@@ -655,15 +693,18 @@ public void testLongNameMd5Hash() throws Exception {
// do I still have the correct modification date?
// let a second elapse so we don't get the current time
Thread.sleep(1000);
- final TarArchiveInputStream tarIn = new TarArchiveInputStream(new ByteArrayInputStream(archive2));
+ final TarArchiveInputStream tarIn = new TarArchiveInputStream(
+ new ByteArrayInputStream(archive2));
final ArchiveEntry nextEntry = tarIn.getNextEntry();
assertEquals(longFileName, nextEntry.getName());
// tar archive stores modification time to second granularity only (floored)
- assertEquals(modificationDate.getTime() / 1000, nextEntry.getLastModifiedDate().getTime() / 1000);
+ assertEquals(modificationDate.getTime() / 1000,
+ nextEntry.getLastModifiedDate().getTime() / 1000);
tarIn.close();
}
- private static byte[] createTarArchiveContainingOneDirectory(final String fname, final Date modificationDate) throws IOException {
+ private static byte[] createTarArchiveContainingOneDirectory(final String fname,
+ final Date modificationDate) throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final TarArchiveOutputStream tarOut = new TarArchiveOutputStream(baos, 1024);
tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);