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 &lt; 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 &lt; 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 (&gt; 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 &gt; 
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


Reply via email to