changeset 399:fdc1db8f7f61 smartgit3

Issue 25: Underflow in InflaterDataAccess; test and fix for hang up when reading past end of compressed data (or zero-length data)
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Sat, 25 Feb 2012 19:31:57 +0100
parents 5e95b0da26f2
children deb64baaa412 31a719b9f95e
files src/org/tmatesoft/hg/internal/DataAccess.java src/org/tmatesoft/hg/internal/InflaterDataAccess.java test-data/test-repos.jar test/org/tmatesoft/hg/test/TestStatus.java
diffstat 4 files changed, 19 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/DataAccess.java	Thu Feb 23 15:31:57 2012 +0100
+++ b/src/org/tmatesoft/hg/internal/DataAccess.java	Sat Feb 25 19:31:57 2012 +0100
@@ -32,6 +32,7 @@
 	public boolean isEmpty() {
 		return true;
 	}
+	// TODO throws IOException (few subclasses have non-trivial length() operation)
 	public int length() {
 		return 0;
 	}
@@ -68,6 +69,10 @@
 		readBytes(b, 0, 4);
 		return b[0] << 24 | (b[1] & 0xFF) << 16 | (b[2] & 0xFF) << 8 | (b[3] & 0xFF);
 	}
+
+	/**
+	 * Read 8 bytes as long value, big-endian.
+	 */
 	public long readLong() throws IOException {
 		byte[] b = new byte[8];
 		readBytes(b, 0, 8);
--- a/src/org/tmatesoft/hg/internal/InflaterDataAccess.java	Thu Feb 23 15:31:57 2012 +0100
+++ b/src/org/tmatesoft/hg/internal/InflaterDataAccess.java	Sat Feb 25 19:31:57 2012 +0100
@@ -158,20 +158,20 @@
 		    int n;
 		    while (len > 0) {
 			    while ((n = inflater.inflate(b, off, len)) == 0) {
-			    	// FIXME few last bytes (checksum?) may be ignored by inflater, thus inflate may return 0 in
+			    	// XXX few last bytes (checksum?) may be ignored by inflater, thus inflate may return 0 in
 			    	// perfectly legal conditions (when all data already expanded, but there are still some bytes
-			    	// in the input stream
-					if (inflater.finished() || inflater.needsDictionary()) {
-	                    throw new EOFException();
-					}
-					if (inflater.needsInput()) {
+			    	// in the input stream)
+					int toRead = -1;
+					if (inflater.needsInput() && (toRead = super.available()) > 0) {
 						// fill:
-						int toRead = super.available();
 						if (toRead > buffer.length) {
 							toRead = buffer.length;
 						}
 						super.readBytes(buffer, 0, toRead);
 						inflater.setInput(buffer, 0, toRead);
+					} else {
+						// prevent hang up in this cycle if no more data is available, see Issue 25
+						throw new EOFException(String.format("No more compressed data is available to satisfy request for %d bytes. [finished:%b, needDict:%b, needInp:%b, available:%d", len, inflater.finished(), inflater.needsDictionary(), inflater.needsInput(), toRead));
 					}
 			    }
 				off += n;
Binary file test-data/test-repos.jar has changed
--- a/test/org/tmatesoft/hg/test/TestStatus.java	Thu Feb 23 15:31:57 2012 +0100
+++ b/test/org/tmatesoft/hg/test/TestStatus.java	Sat Feb 25 19:31:57 2012 +0100
@@ -537,6 +537,10 @@
 	 *  b) That FilterDataAccess (with 0 size represents patch more or less relevantly, but didn't represent actual revision) get successfully
 	 *     reassigned as lastUserData for the next iteration. And at the next step attempt to apply patch recorded in the next revision failed
 	 *     because baseRevisionData is 0 length FilterDataAccess
+	 * 
+	 * Same applies for 
+	 * Issue 25: IOException: Underflow. Rewind past end of the slice in InflaterDataAccess
+	 * with the difference in separate .i and .d (thus not 0 but 'x' first byte was read)
 	 *
 	 * Sample:
 	 *  status-5/file1 has 3 revisions, second is zero-length patch:
@@ -551,10 +555,12 @@
 	@Test
 	public void testZeroLengthPatchAgainstNonEmptyBaseRev() throws Exception{
 		repo = Configuration.get().find("status-5");
-		// pretend we modified file in the working copy
+		// pretend we modified files in the working copy
 		// for HgWorkingCopyStatusCollector to go and retrieve its content from repository 
 		File f1 = new File(repo.getWorkingDir(), "file1");
 		f1.setLastModified(System.currentTimeMillis());
+		File f3 = new File(repo.getWorkingDir(), "file3");
+		f3.setLastModified(System.currentTimeMillis());
 		//
 		HgStatusCommand cmd = new HgStatusCommand(repo);
 		cmd.all();