LUCENE-6577: Give earlier and better error message for invalid CRC

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1685927 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Muir 2015-06-17 02:49:35 +00:00
parent 5a0c012250
commit f047b54425
3 changed files with 117 additions and 3 deletions

View File

@ -85,6 +85,9 @@ New Features
quite slow and are only fast in specific cases. (Adrien Grand, quite slow and are only fast in specific cases. (Adrien Grand,
Robert Muir, Mike McCandless) Robert Muir, Mike McCandless)
* LUCENE-6577: Give earlier and better error message for invalid CRC.
(Robert Muir)
API Changes API Changes
* LUCENE-6508: Simplify Lock api, there is now just * LUCENE-6508: Simplify Lock api, there is now just

View File

@ -307,7 +307,7 @@ public final class CodecUtil {
public static void writeFooter(IndexOutput out) throws IOException { public static void writeFooter(IndexOutput out) throws IOException {
out.writeInt(FOOTER_MAGIC); out.writeInt(FOOTER_MAGIC);
out.writeInt(0); out.writeInt(0);
out.writeLong(out.getChecksum()); writeCRC(out);
} }
/** /**
@ -330,7 +330,7 @@ public final class CodecUtil {
public static long checkFooter(ChecksumIndexInput in) throws IOException { public static long checkFooter(ChecksumIndexInput in) throws IOException {
validateFooter(in); validateFooter(in);
long actualChecksum = in.getChecksum(); long actualChecksum = in.getChecksum();
long expectedChecksum = in.readLong(); long expectedChecksum = readCRC(in);
if (expectedChecksum != actualChecksum) { if (expectedChecksum != actualChecksum) {
throw new CorruptIndexException("checksum failed (hardware problem?) : expected=" + Long.toHexString(expectedChecksum) + throw new CorruptIndexException("checksum failed (hardware problem?) : expected=" + Long.toHexString(expectedChecksum) +
" actual=" + Long.toHexString(actualChecksum), in); " actual=" + Long.toHexString(actualChecksum), in);
@ -399,7 +399,7 @@ public final class CodecUtil {
public static long retrieveChecksum(IndexInput in) throws IOException { public static long retrieveChecksum(IndexInput in) throws IOException {
in.seek(in.length() - footerLength()); in.seek(in.length() - footerLength());
validateFooter(in); validateFooter(in);
return in.readLong(); return readCRC(in);
} }
private static void validateFooter(IndexInput in) throws IOException { private static void validateFooter(IndexInput in) throws IOException {
@ -436,4 +436,30 @@ public final class CodecUtil {
in.seek(in.length() - footerLength()); in.seek(in.length() - footerLength());
return checkFooter(in); return checkFooter(in);
} }
/**
* Reads CRC32 value as a 64-bit long from the input.
* @throws CorruptIndexException if CRC is formatted incorrectly (wrong bits set)
* @throws IOException if an i/o error occurs
*/
public static long readCRC(IndexInput input) throws IOException {
long value = input.readLong();
if ((value & 0xFFFFFFFF00000000L) != 0) {
throw new CorruptIndexException("Illegal CRC-32 checksum: " + value, input);
}
return value;
}
/**
* Writes CRC32 value as a 64-bit long to the output.
* @throws IllegalStateException if CRC is formatted incorrectly (wrong bits set)
* @throws IOException if an i/o error occurs
*/
public static void writeCRC(IndexOutput output) throws IOException {
long value = output.getChecksum();
if ((value & 0xFFFFFFFF00000000L) != 0) {
throw new IllegalStateException("Illegal CRC-32 checksum: " + value + " (resource=" + output + ")");
}
output.writeLong(value);
}
} }

View File

@ -17,6 +17,9 @@ package org.apache.lucene.index;
* limitations under the License. * limitations under the License.
*/ */
import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.lucene.codecs.CodecUtil; import org.apache.lucene.codecs.CodecUtil;
import org.apache.lucene.store.BufferedChecksumIndexInput; import org.apache.lucene.store.BufferedChecksumIndexInput;
import org.apache.lucene.store.ChecksumIndexInput; import org.apache.lucene.store.ChecksumIndexInput;
@ -252,4 +255,86 @@ public class TestCodecUtil extends LuceneTestCase {
// expected // expected
} }
} }
public void testReadBogusCRC() throws Exception {
RAMFile file = new RAMFile();
IndexOutput output = new RAMOutputStream(file, false);
output.writeLong(-1L); // bad
output.writeLong(1L << 32); // bad
output.writeLong(-(1L << 32)); // bad
output.writeLong((1L << 32) - 1); // ok
output.close();
IndexInput input = new RAMInputStream("file", file);
// read 3 bogus values
for (int i = 0; i < 3; i++) {
try {
CodecUtil.readCRC(input);
fail("didn't get expected exception");
} catch (CorruptIndexException expected) {
// expected
}
}
// good value
CodecUtil.readCRC(input);
}
public void testWriteBogusCRC() throws Exception {
RAMFile file = new RAMFile();
final IndexOutput output = new RAMOutputStream(file, false);
AtomicLong fakeChecksum = new AtomicLong();
// wrap the index input where we control the checksum for mocking
IndexOutput fakeOutput = new IndexOutput("fake") {
@Override
public void close() throws IOException {
output.close();
}
@Override
public long getFilePointer() {
return output.getFilePointer();
}
@Override
public long getChecksum() throws IOException {
return fakeChecksum.get();
}
@Override
public void writeByte(byte b) throws IOException {
output.writeByte(b);
}
@Override
public void writeBytes(byte[] b, int offset, int length) throws IOException {
output.writeBytes(b, offset, length);
}
};
fakeChecksum.set(-1L); // bad
try {
CodecUtil.writeCRC(fakeOutput);
fail("didn't get expected exception");
} catch (IllegalStateException expected) {
// expected exception
}
fakeChecksum.set(1L << 32); // bad
try {
CodecUtil.writeCRC(fakeOutput);
fail("didn't get expected exception");
} catch (IllegalStateException expected) {
// expected exception
}
fakeChecksum.set(-(1L << 32)); // bad
try {
CodecUtil.writeCRC(fakeOutput);
fail("didn't get expected exception");
} catch (IllegalStateException expected) {
// expected exception
}
fakeChecksum.set((1L << 32) - 1); // ok
CodecUtil.writeCRC(fakeOutput);
}
} }