Author: bodewig Date: Thu Dec 8 14:03:57 2011 New Revision: 1211892 URL: http://svn.apache.org/viewvc?rev=1211892&view=rev Log: support writing big files using star extensions. COMPRESS-165. Patch by John Kodis.
Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveEntryTest.java commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java?rev=1211892&r1=1211891&r2=1211892&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java Thu Dec 8 14:03:57 2011 @@ -561,26 +561,11 @@ public class TarArchiveEntry implements * Set this entry's file size. * * @param size This entry's new file size. - * @throws IllegalArgumentException if the size is < 0 - * or > {@link TarConstants#MAXSIZE} (077777777777L). + * @throws IllegalArgumentException if the size is < 0. */ public void setSize(long size) { - if (size > MAXSIZE || size < 0){ - throw new IllegalArgumentException("Size is out of range: "+size); - } - this.size = size; - } - - /** - * Set this entry's file size. - * - * <p>Invoked by input stream when reading a PAX header.</p> - * @throws IllegalArgumentException if the size is < 0 - * @since Apache Commons Compress 1.4 - */ - void adjustSize(long size) { if (size < 0){ - throw new IllegalArgumentException("Size is out of range: " + size); + throw new IllegalArgumentException("Size is out of range: "+size); } this.size = size; } @@ -751,16 +736,35 @@ public class TarArchiveEntry implements /** * Write an entry's header information to a header buffer. * + * <p>This method does not use the star/GNU tar/BSD tar extensions.</p> + * * @param outbuf The tar entry header buffer to fill in. */ public void writeEntryHeader(byte[] outbuf) { + writeEntryHeader(outbuf, false); + } + + /** + * Write an entry's header information to a header buffer. + * + * @param outbuf The tar entry header buffer to fill in. + * @param starMode whether to use the star/GNU tar/BSD tar + * extension for the size field if the size is bigger than 8GiB + * @since Apache Commons Compress 1.4 + */ + public void writeEntryHeader(byte[] outbuf, boolean starMode) { int offset = 0; offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN); offset = TarUtils.formatOctalBytes(mode, outbuf, offset, MODELEN); offset = TarUtils.formatOctalBytes(userId, outbuf, offset, UIDLEN); offset = TarUtils.formatOctalBytes(groupId, outbuf, offset, GIDLEN); - offset = TarUtils.formatLongOctalBytes(size, outbuf, offset, SIZELEN); + if (size > TarConstants.MAXSIZE && !starMode) { + // size is in PAX header + offset = TarUtils.formatLongOctalBytes(0, outbuf, offset, SIZELEN); + } else { + offset = TarUtils.formatLongOctalOrBinaryBytes(size, outbuf, offset, SIZELEN); + } offset = TarUtils.formatLongOctalBytes(modTime, outbuf, offset, MODTIMELEN); int csOffset = offset; Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java?rev=1211892&r1=1211891&r2=1211892&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java Thu Dec 8 14:03:57 2011 @@ -356,7 +356,7 @@ public class TarArchiveInputStream exten } else if ("uname".equals(key)){ currEntry.setUserName(val); } else if ("size".equals(key)){ - currEntry.adjustSize(Long.parseLong(val)); + currEntry.setSize(Long.parseLong(val)); } } } Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java?rev=1211892&r1=1211891&r2=1211892&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java Thu Dec 8 14:03:57 2011 @@ -42,6 +42,15 @@ public class TarArchiveOutputStream exte /** GNU tar extensions are used to store long file names in the archive. */ public static final int LONGFILE_GNU = 2; + /** Fail if a big file (> 8GiB) is required in the archive. */ + public static final int BIGFILE_ERROR = 0; + + /** star/GNU tar/BSD tar extensions are used to store big file sizes in the archive. */ + public static final int BIGFILE_STAR = 1; + + /** POSIX/PAX extensions are used to store big file sizes in the archive. */ + public static final int BIGFILE_POSIX = 2; + private long currSize; private String currName; private long currBytes; @@ -50,6 +59,7 @@ public class TarArchiveOutputStream exte private final byte[] assemBuf; protected final TarBuffer buffer; private int longFileMode = LONGFILE_ERROR; + private int bigFileMode = BIGFILE_ERROR; private boolean closed = false; @@ -104,6 +114,18 @@ public class TarArchiveOutputStream exte this.longFileMode = longFileMode; } + /** + * Set the big file mode. + * This can be BIGFILE_ERROR(0), BIGFILE_POSIX(1) or BIGFILE_STAR(2). + * This specifies the treatment of big files (sizes > TarConstants.MAXSIZE). + * Default is BIGFILE_ERROR. + * @param bigFileMode the mode to use + * @since Apache Commons Compress 1.4 + */ + public void setBigFileMode(int bigFileMode) { + this.bigFileMode = bigFileMode; + } + @Deprecated @Override @@ -205,8 +227,15 @@ public class TarArchiveOutputStream exte + TarConstants.NAMELEN + " bytes)"); } } + if (entry.getSize() > TarConstants.MAXSIZE) { + if (bigFileMode != BIGFILE_STAR) { + throw new RuntimeException("file size '" + entry.getSize() + + "' is too big ( > " + + TarConstants.MAXSIZE + " bytes)"); + } + } - entry.writeEntryHeader(recordBuf); + entry.writeEntryHeader(recordBuf, bigFileMode == BIGFILE_STAR); buffer.writeRecord(recordBuf); currBytes = 0; Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java?rev=1211892&r1=1211891&r2=1211892&view=diff ============================================================================== --- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java (original) +++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java Thu Dec 8 14:03:57 2011 @@ -121,6 +121,7 @@ public class TarUtils { * missing or an invalid byte is detected in an octal number, or * if a binary number would exceed the size of a signed long * 64-bit integer. + * @since Apache Commons Compress 1.4 */ public static long parseOctalOrBinary(final byte[] buffer, final int offset, final int length) { @@ -304,6 +305,45 @@ public class TarUtils { } /** + * Write an long integer into a buffer as an octal string if this + * will fit, or as a binary number otherwise. + * + * Uses {@link #formatUnsignedOctalString} to format + * the value as an octal string with leading zeros. + * The converted number is followed by a space. + * + * @param value The value to write into the buffer. + * @param buf The destination buffer. + * @param offset The starting offset into the buffer. + * @param length The length of the buffer. + * @return The updated offset. + * @throws IllegalArgumentException if the value (and trailer) + * will not fit in the buffer. + * @since Apache Commons Compress 1.4 + */ + public static int formatLongOctalOrBinaryBytes( + final long value, byte[] buf, final int offset, final int length) { + + if (value < TarConstants.MAXSIZE + 1) { + return formatLongOctalBytes(value, buf, offset, length); + } + + long val = value; + for (int i = offset + length - 1; i >= offset; i--) { + buf[i] = (byte) val; + val >>= 8; + } + + if (val != 0 || (buf[offset] & 0x80) != 0) { + throw new IllegalArgumentException("Value " + value + + " is too large for " + length + " byte field."); + } + + buf[offset] |= 0x80; + return offset + length; + } + + /** * Writes an octal value into a buffer. * * Uses {@link #formatUnsignedOctalString} to format Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveEntryTest.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveEntryTest.java?rev=1211892&r1=1211891&r2=1211892&view=diff ============================================================================== --- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveEntryTest.java (original) +++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveEntryTest.java Thu Dec 8 14:03:57 2011 @@ -109,23 +109,6 @@ public class TarArchiveEntryTest extends } catch (IllegalArgumentException expected) { } t.setSize(077777777777L); - try { - t.setSize(0100000000000L); - fail("Should have generated IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - } - - public void testAdjustFileSize(){ - TarArchiveEntry t = new TarArchiveEntry(""); - t.adjustSize(0); - t.adjustSize(1); - try { - t.adjustSize(-1); - fail("Should have generated IllegalArgumentException"); - } catch (IllegalArgumentException expected) { - } - t.adjustSize(077777777777L); - t.adjustSize(0100000000000L); + t.setSize(0100000000000L); } } Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java?rev=1211892&r1=1211891&r2=1211892&view=diff ============================================================================== --- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java (original) +++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStreamTest.java Thu Dec 8 14:03:57 2011 @@ -18,6 +18,8 @@ package org.apache.commons.compress.archivers.tar; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -54,4 +56,41 @@ public class TarArchiveOutputStreamTest assertEquals(f.length(), tarOut.getBytesWritten()); } + + public void testMaxFileSizeError() throws Exception { + TarArchiveEntry t = new TarArchiveEntry("foo"); + t.setSize(077777777777L); + TarArchiveOutputStream tos = + new TarArchiveOutputStream(new ByteArrayOutputStream()); + tos.putArchiveEntry(t); + t.setSize(0100000000000L); + tos = new TarArchiveOutputStream(new ByteArrayOutputStream()); + try { + tos.putArchiveEntry(t); + fail("Should have generated RuntimeException"); + } catch (RuntimeException expected) { + } + } + + public void testBigFileStarMode() throws Exception { + TarArchiveEntry t = new TarArchiveEntry("foo"); + t.setSize(0100000000000L); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + TarArchiveOutputStream tos = new TarArchiveOutputStream(bos); + tos.setBigFileMode(TarArchiveOutputStream.BIGFILE_STAR); + tos.putArchiveEntry(t); + // make sure header is written to byte array + tos.write(new byte[10 * 1024]); + byte[] data = bos.toByteArray(); + assertEquals(0x80, + ((int) data[TarConstants.NAMELEN + + TarConstants.MODELEN + + TarConstants.UIDLEN + + TarConstants.GIDLEN] + ) & 0x80); + TarArchiveInputStream tin = + new TarArchiveInputStream(new ByteArrayInputStream(data)); + TarArchiveEntry e = tin.getNextTarEntry(); + assertEquals(0100000000000L, e.getSize()); + } } \ No newline at end of file