diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index de175441bce8..992c9f49b379 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -14,6 +14,7 @@ import hudson.util.FormFieldValidator; import hudson.util.IOException2; import hudson.util.StreamResource; +import hudson.util.HeadBufferingStream; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; @@ -990,10 +991,17 @@ private Integer writeToTar(File baseDir, String fileMask, String excludes, Pipe /** * Reads from a tar stream and stores obtained files to the base dir. */ - private static void readFromTar(String name, File baseDir, InputStream in) throws IOException { + private static void readFromTar(String name, File baseDir, InputStream _in) throws IOException { Untar untar = new Untar(); untar.setProject(new Project()); - untar.add(new StreamResource(name,new BufferedInputStream(new GZIPInputStream(in)))); + HeadBufferingStream in = new HeadBufferingStream(_in,SIDE_BUFFER_SIZE); + try { + untar.add(new StreamResource(name,new BufferedInputStream(new GZIPInputStream(in)))); + } catch (IOException e) { + // various people reported "java.io.IOException: Not in GZIP format" here, so diagnose this problem better + in.fillSide(); + throw new IOException2(e.getMessage()+"\nstream="+Util.toHexString(in.getSideBuffer()),e); + } untar.setDest(baseDir); try { untar.execute(); @@ -1187,6 +1195,8 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound private static final long serialVersionUID = 1L; + public static int SIDE_BUFFER_SIZE = 1024; + /** * Adapts {@link FileCallable} to {@link Callable}. */ diff --git a/core/src/main/java/hudson/util/HeadBufferingStream.java b/core/src/main/java/hudson/util/HeadBufferingStream.java new file mode 100644 index 000000000000..c63498cff6dd --- /dev/null +++ b/core/src/main/java/hudson/util/HeadBufferingStream.java @@ -0,0 +1,69 @@ +package hudson.util; + +import org.apache.commons.io.output.ByteArrayOutputStream; + +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + * {@link FilterInputStream} that buffers the first N bytes to a byte array on the side. + * This byte array can be then accessed later. + * + *

+ * Useful for sniffing the content of the stream after the error is discovered. + * + * @author Kohsuke Kawaguchi + */ +public class HeadBufferingStream extends FilterInputStream { + private final ByteArrayOutputStream side; + private final int sideBufferSize; + + public HeadBufferingStream(InputStream in, int sideBufferSize) { + super(in); + this.sideBufferSize = sideBufferSize; + this.side = new ByteArrayOutputStream(sideBufferSize); + } + + public int read() throws IOException { + int i = in.read(); + if(i>=0 && space()>0) + side.write(i); + return i; + } + + public int read(byte b[], int off, int len) throws IOException { + int r = in.read(b, off, len); + if(r>0) { + int sp = space(); + if(sp>0) + side.write(b,off,Math.min(r, sp)); + } + return r; + } + + /** + * Available space in the {@link #side} buffer. + */ + private int space() { + return sideBufferSize-side.size(); + } + + /** + * Read until we fill up the side buffer. + */ + public void fillSide() throws IOException { + byte[] buf = new byte[space()]; + while(space()>0) { + if(read(buf)<0) + return; + } + } + + /** + * Gets the side buffer content. + */ + public byte[] getSideBuffer() { + return side.toByteArray(); + } +}