|
26 | 26 | import org.apache.lucene.index.IndexCommit;
|
27 | 27 | import org.apache.lucene.store.NIOFSDirectory;
|
28 | 28 | import org.apache.lucene.util.LuceneTestCase;
|
| 29 | +import org.elasticsearch.common.io.stream.InputStreamStreamInput; |
29 | 30 | import org.elasticsearch.core.internal.io.IOUtils;
|
30 | 31 | import org.elasticsearch.index.engine.CombinedDeletionPolicy;
|
31 | 32 |
|
32 | 33 | import java.io.IOException;
|
33 | 34 | import java.nio.ByteBuffer;
|
| 35 | +import java.nio.channels.Channels; |
34 | 36 | import java.nio.channels.FileChannel;
|
35 | 37 | import java.nio.file.DirectoryStream;
|
36 | 38 | import java.nio.file.Files;
|
|
47 | 49 | import java.util.regex.Pattern;
|
48 | 50 |
|
49 | 51 | import static org.elasticsearch.index.translog.Translog.CHECKPOINT_FILE_NAME;
|
| 52 | +import static org.elasticsearch.index.translog.Translog.TRANSLOG_FILE_SUFFIX; |
50 | 53 | import static org.hamcrest.MatcherAssert.assertThat;
|
51 | 54 | import static org.hamcrest.Matchers.empty;
|
52 | 55 | import static org.hamcrest.Matchers.equalTo;
|
@@ -151,26 +154,29 @@ static void corruptFile(Logger logger, Random random, Path fileToCorrupt, boolea
|
151 | 154 | final long corruptPosition = RandomNumbers.randomLongBetween(random, 0, fileSize - 1);
|
152 | 155 |
|
153 | 156 | if (random.nextBoolean()) {
|
154 |
| - // read |
155 |
| - fileChannel.position(corruptPosition); |
156 |
| - assertThat(fileChannel.position(), equalTo(corruptPosition)); |
157 |
| - ByteBuffer bb = ByteBuffer.wrap(new byte[1]); |
158 |
| - fileChannel.read(bb); |
159 |
| - bb.flip(); |
160 |
| - |
161 |
| - // corrupt |
162 |
| - byte oldValue = bb.get(0); |
163 |
| - byte newValue; |
164 | 157 | do {
|
165 |
| - newValue = (byte) random.nextInt(0x100); |
166 |
| - } while (newValue == oldValue); |
167 |
| - bb.put(0, newValue); |
168 |
| - |
169 |
| - // rewrite |
170 |
| - fileChannel.position(corruptPosition); |
171 |
| - fileChannel.write(bb); |
172 |
| - logger.info("corruptFile: corrupting file {} at position {} turning 0x{} into 0x{}", fileToCorrupt, corruptPosition, |
173 |
| - Integer.toHexString(oldValue & 0xff), Integer.toHexString(newValue & 0xff)); |
| 158 | + // read |
| 159 | + fileChannel.position(corruptPosition); |
| 160 | + assertThat(fileChannel.position(), equalTo(corruptPosition)); |
| 161 | + ByteBuffer bb = ByteBuffer.wrap(new byte[1]); |
| 162 | + fileChannel.read(bb); |
| 163 | + bb.flip(); |
| 164 | + |
| 165 | + // corrupt |
| 166 | + byte oldValue = bb.get(0); |
| 167 | + byte newValue; |
| 168 | + do { |
| 169 | + newValue = (byte) random.nextInt(0x100); |
| 170 | + } while (newValue == oldValue); |
| 171 | + bb.put(0, newValue); |
| 172 | + |
| 173 | + // rewrite |
| 174 | + fileChannel.position(corruptPosition); |
| 175 | + fileChannel.write(bb); |
| 176 | + logger.info("corruptFile: corrupting file {} at position {} turning 0x{} into 0x{}", fileToCorrupt, corruptPosition, |
| 177 | + Integer.toHexString(oldValue & 0xff), Integer.toHexString(newValue & 0xff)); |
| 178 | + } while (isTranslogHeaderVersionFlipped(fileToCorrupt, fileChannel)); |
| 179 | + |
174 | 180 | } else {
|
175 | 181 | logger.info("corruptFile: truncating file {} from length {} to length {}", fileToCorrupt, fileSize, corruptPosition);
|
176 | 182 | fileChannel.truncate(corruptPosition);
|
@@ -233,4 +239,22 @@ public void close() {
|
233 | 239 | }
|
234 | 240 | };
|
235 | 241 | }
|
| 242 | + |
| 243 | + /** |
| 244 | + * An old translog header does not have a checksum. If we flip the header version of an empty translog from 3 to 2, |
| 245 | + * then we won't detect that corruption, and the translog will be considered clean as before. |
| 246 | + */ |
| 247 | + static boolean isTranslogHeaderVersionFlipped(Path corruptedFile, FileChannel channel) throws IOException { |
| 248 | + if (corruptedFile.toString().endsWith(TRANSLOG_FILE_SUFFIX) == false) { |
| 249 | + return false; |
| 250 | + } |
| 251 | + channel.position(0); |
| 252 | + final InputStreamStreamInput in = new InputStreamStreamInput(Channels.newInputStream(channel), channel.size()); |
| 253 | + try { |
| 254 | + final int version = TranslogHeader.readHeaderVersion(corruptedFile, channel, in); |
| 255 | + return version == TranslogHeader.VERSION_CHECKPOINTS; |
| 256 | + } catch (IllegalStateException | TranslogCorruptedException | IOException e) { |
| 257 | + return false; |
| 258 | + } |
| 259 | + } |
236 | 260 | }
|
0 commit comments