Don't test corruption detection within CFS checksum (#33911)

Closes #33881
This commit is contained in:
Vladimir Dolzhenko 2018-09-22 10:21:36 +02:00 committed by GitHub
parent 17605bf1c6
commit 477391d751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 16 deletions

View File

@ -78,24 +78,19 @@ public final class CorruptionUtils {
checksumBeforeCorruption = CodecUtil.retrieveChecksum(input); checksumBeforeCorruption = CodecUtil.retrieveChecksum(input);
} }
try (FileChannel raf = FileChannel.open(fileToCorrupt, StandardOpenOption.READ, StandardOpenOption.WRITE)) { try (FileChannel raf = FileChannel.open(fileToCorrupt, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
// read long maxPosition = raf.size();
raf.position(random.nextInt((int) Math.min(Integer.MAX_VALUE, raf.size())));
long filePointer = raf.position();
ByteBuffer bb = ByteBuffer.wrap(new byte[1]);
raf.read(bb);
bb.flip();
// corrupt if (fileToCorrupt.getFileName().toString().endsWith(".cfs") && maxPosition > 4) {
byte oldValue = bb.get(0); // TODO: it is known that Lucene does not check the checksum of CFS file (CompoundFileS, like an archive)
byte newValue = (byte) (oldValue + 1); // see note at https://github.com/elastic/elasticsearch/pull/33911
bb.put(0, newValue); // so far, don't corrupt crc32 part of checksum (last 4 bytes) of cfs file
// checksum is 8 bytes: first 4 bytes have to be zeros, while crc32 value is not verified
// rewrite maxPosition -= 4;
raf.position(filePointer); }
raf.write(bb); final int position = random.nextInt((int) Math.min(Integer.MAX_VALUE, maxPosition));
logger.info("Corrupting file -- flipping at position {} from {} to {} file: {}", filePointer, corruptAt(fileToCorrupt, raf, position);
Integer.toHexString(oldValue), Integer.toHexString(newValue), fileToCorrupt.getFileName());
} }
long checksumAfterCorruption; long checksumAfterCorruption;
long actualChecksumAfterCorruption; long actualChecksumAfterCorruption;
try (ChecksumIndexInput input = dir.openChecksumInput(fileToCorrupt.getFileName().toString(), IOContext.DEFAULT)) { try (ChecksumIndexInput input = dir.openChecksumInput(fileToCorrupt.getFileName().toString(), IOContext.DEFAULT)) {
@ -120,5 +115,25 @@ public final class CorruptionUtils {
} }
} }
static void corruptAt(Path path, FileChannel channel, int position) throws IOException {
// read
channel.position(position);
long filePointer = channel.position();
ByteBuffer bb = ByteBuffer.wrap(new byte[1]);
channel.read(bb);
bb.flip();
// corrupt
byte oldValue = bb.get(0);
byte newValue = (byte) (oldValue + 1);
bb.put(0, newValue);
// rewrite
channel.position(filePointer);
channel.write(bb);
logger.info("Corrupting file -- flipping at position {} from {} to {} file: {}", filePointer,
Integer.toHexString(oldValue), Integer.toHexString(newValue), path.getFileName());
}
} }

View File

@ -0,0 +1,81 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.test;
import org.apache.lucene.index.CheckIndex;
import org.apache.lucene.store.SimpleFSDirectory;
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
import org.elasticsearch.index.shard.IndexShard;
import org.elasticsearch.index.shard.IndexShardTestCase;
import org.elasticsearch.index.shard.ShardPath;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.stream.Stream;
import static org.elasticsearch.test.CorruptionUtils.corruptAt;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.lessThan;
public class CorruptionUtilsTests extends IndexShardTestCase {
/**
* There is a dependency on Lucene bug fix
* https://github.com/elastic/elasticsearch/pull/33911
*/
public void testLuceneCheckIndexIgnoresLast4Bytes() throws Exception {
final IndexShard indexShard = newStartedShard(true);
final long numDocs = between(10, 100);
for (long i = 0; i < numDocs; i++) {
indexDoc(indexShard, "_doc", Long.toString(i), "{}");
}
indexShard.flush(new FlushRequest());
closeShards(indexShard);
final ShardPath shardPath = indexShard.shardPath();
final Path indexPath = shardPath.getDataPath().resolve(ShardPath.INDEX_FOLDER_NAME);
final Path cfsFile;
try (Stream<Path> paths = Files.walk(indexPath)) {
cfsFile = paths.filter(p -> p.getFileName().toString().endsWith(".cfs")).findFirst()
.orElseThrow(() -> new IllegalStateException("CFS file has to be there"));
}
try (FileChannel raf = FileChannel.open(cfsFile, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
assertThat(raf.size(), lessThan(Integer.MAX_VALUE * 1L));
final int maxPosition = (int) raf.size();
// corrupt only last 4 bytes!
final int position = randomIntBetween(maxPosition - 4, maxPosition - 1);
corruptAt(cfsFile, raf, position);
}
final CheckIndex.Status status;
try (CheckIndex checkIndex = new CheckIndex(new SimpleFSDirectory(indexPath))) {
status = checkIndex.checkIndex();
}
assertThat("That's a good news! "
+ "Lucene now validates CRC32 of CFS file: time to drop workaround at CorruptionUtils (and this test)",
status.clean, equalTo(true));
}
}