diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index bb369b85e87..77dce67a602 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -150,6 +150,10 @@ API Changes provide them lazily. TermsInSetQuery switches to using this method to report matching terms. (Alan Woodward) +* LUCENE-7822: CodecUtil#checkFooter(IndexInput, Throwable) now throws a + CorruptIndexException if checksums mismatch or if checksums can't be verified. + (Martin Amirault, Adrien Grand) + New Features --------------------- (No changes) diff --git a/lucene/core/src/java/org/apache/lucene/codecs/CodecUtil.java b/lucene/core/src/java/org/apache/lucene/codecs/CodecUtil.java index c49946b7ffb..2e736c1fda5 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/CodecUtil.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/CodecUtil.java @@ -448,24 +448,27 @@ public final class CodecUtil { checkFooter(in); } else { try { + // If we have evidence of corruption then we return the corruption as the + // main exception and the prior exception gets suppressed. Otherwise we + // return the prior exception with a suppressed exception that notifies + // the user that checksums matched. long remaining = in.length() - in.getFilePointer(); if (remaining < footerLength()) { // corruption caused us to read into the checksum footer already: we can't proceed - priorException.addSuppressed(new CorruptIndexException("checksum status indeterminate: remaining=" + remaining + - ", please run checkindex for more details", in)); + throw new CorruptIndexException("checksum status indeterminate: remaining=" + remaining + + "; please run checkindex for more details", in); } else { // otherwise, skip any unread bytes. in.skipBytes(remaining - footerLength()); // now check the footer - try { - long checksum = checkFooter(in); - priorException.addSuppressed(new CorruptIndexException("checksum passed (" + Long.toHexString(checksum) + - "). possibly transient resource issue, or a Lucene or JVM bug", in)); - } catch (CorruptIndexException t) { - priorException.addSuppressed(t); - } + long checksum = checkFooter(in); + priorException.addSuppressed(new CorruptIndexException("checksum passed (" + Long.toHexString(checksum) + + "). possibly transient resource issue, or a Lucene or JVM bug", in)); } + } catch (CorruptIndexException corruptException) { + corruptException.addSuppressed(priorException); + throw corruptException; } catch (Throwable t) { // catch-all for things that shouldn't go wrong (e.g. OOM during readInt) but could... priorException.addSuppressed(new CorruptIndexException("checksum status indeterminate: unexpected exception", in, t)); diff --git a/lucene/core/src/test/org/apache/lucene/codecs/TestCodecUtil.java b/lucene/core/src/test/org/apache/lucene/codecs/TestCodecUtil.java index 0a11a9b5b49..94c82aa34f3 100644 --- a/lucene/core/src/test/org/apache/lucene/codecs/TestCodecUtil.java +++ b/lucene/core/src/test/org/apache/lucene/codecs/TestCodecUtil.java @@ -148,13 +148,13 @@ public class TestCodecUtil extends LuceneTestCase { // bogusly read a byte too far (can happen) input.readByte(); Exception mine = new RuntimeException("fake exception"); - RuntimeException expected = expectThrows(RuntimeException.class, () -> { + CorruptIndexException expected = expectThrows(CorruptIndexException.class, () -> { CodecUtil.checkFooter(input, mine); }); - assertEquals("fake exception", expected.getMessage()); + assertTrue(expected.getMessage().contains("checksum status indeterminate")); Throwable suppressed[] = expected.getSuppressed(); assertEquals(1, suppressed.length); - assertTrue(suppressed[0].getMessage().contains("checksum status indeterminate")); + assertEquals("fake exception", suppressed[0].getMessage()); input.close(); } @@ -172,13 +172,13 @@ public class TestCodecUtil extends LuceneTestCase { CodecUtil.checkHeader(input, "FooBar", 5, 5); assertEquals("this is the data", input.readString()); Exception mine = new RuntimeException("fake exception"); - RuntimeException expected = expectThrows(RuntimeException.class, () -> { + CorruptIndexException expected = expectThrows(CorruptIndexException.class, () -> { CodecUtil.checkFooter(input, mine); }); - assertEquals("fake exception", expected.getMessage()); + assertTrue(expected.getMessage().contains("checksum failed")); Throwable suppressed[] = expected.getSuppressed(); assertEquals(1, suppressed.length); - assertTrue(suppressed[0].getMessage().contains("checksum failed")); + assertEquals("fake exception", suppressed[0].getMessage()); input.close(); } diff --git a/lucene/core/src/test/org/apache/lucene/util/TestOfflineSorter.java b/lucene/core/src/test/org/apache/lucene/util/TestOfflineSorter.java index 927af148afc..f902cd58cc0 100644 --- a/lucene/core/src/test/org/apache/lucene/util/TestOfflineSorter.java +++ b/lucene/core/src/test/org/apache/lucene/util/TestOfflineSorter.java @@ -17,7 +17,6 @@ package org.apache.lucene.util; -import java.io.EOFException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; @@ -351,14 +350,12 @@ public class TestOfflineSorter extends LuceneTestCase { IndexOutput unsorted = dir.createTempOutput("unsorted", "tmp", IOContext.DEFAULT); writeAll(unsorted, generateFixed(5*1024)); - // This corruption made OfflineSorter fail with its own exception, but we verify it also went and added (as suppressed) that the - // checksum was wrong: - EOFException e = expectThrows(EOFException.class, () -> { + // This corruption made OfflineSorter fail with its own exception, but we verify and throw a CorruptIndexException + // instead when checksums don't match. + CorruptIndexException e = expectThrows(CorruptIndexException.class, () -> { new OfflineSorter(dir, "foo").sort(unsorted.getName()); }); - assertEquals(1, e.getSuppressed().length); - assertTrue(e.getSuppressed()[0] instanceof CorruptIndexException); - assertTrue(e.getSuppressed()[0].getMessage().contains("checksum failed (hardware problem?)")); + assertTrue(e.getMessage().contains("checksum failed (hardware problem?)")); } } @@ -436,12 +433,10 @@ public class TestOfflineSorter extends LuceneTestCase { IndexOutput unsorted = dir.createTempOutput("unsorted", "tmp", IOContext.DEFAULT); writeAll(unsorted, generateFixed((int) (OfflineSorter.MB * 3))); - EOFException e = expectThrows(EOFException.class, () -> { + CorruptIndexException e = expectThrows(CorruptIndexException.class, () -> { new OfflineSorter(dir, "foo", OfflineSorter.DEFAULT_COMPARATOR, BufferSize.megabytes(1), 10, -1, null, 0).sort(unsorted.getName()); }); - assertEquals(1, e.getSuppressed().length); - assertTrue(e.getSuppressed()[0] instanceof CorruptIndexException); - assertTrue(e.getSuppressed()[0].getMessage().contains("checksum failed (hardware problem?)")); + assertTrue(e.getMessage().contains("checksum failed (hardware problem?)")); } }