| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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.apache.lucene.store; |
| |
| import java.io.IOException; |
| |
| /** Corrupts on bit of a file after close */ |
| public class CorruptingIndexOutput extends IndexOutput { |
| protected final IndexOutput out; |
| final Directory dir; |
| final long byteToCorrupt; |
| private boolean closed; |
| |
| public CorruptingIndexOutput(Directory dir, long byteToCorrupt, IndexOutput out) { |
| super("CorruptingIndexOutput(" + out + ")", out.getName()); |
| this.dir = dir; |
| this.byteToCorrupt = byteToCorrupt; |
| this.out = out; |
| } |
| |
| @Override |
| public String getName() { |
| return out.getName(); |
| } |
| |
| @Override |
| public void close() throws IOException { |
| if (closed == false) { |
| out.close(); |
| // NOTE: must corrupt after file is closed, because if we corrupt "inlined" (as bytes are being written) the checksum sees the wrong |
| // bytes and is "correct"!! |
| corruptFile(); |
| closed = true; |
| } |
| } |
| |
| protected void corruptFile() throws IOException { |
| // Now corrupt the specfied byte: |
| String newTempName; |
| try(IndexOutput tmpOut = dir.createTempOutput("tmp", "tmp", IOContext.DEFAULT); |
| IndexInput in = dir.openInput(out.getName(), IOContext.DEFAULT)) { |
| newTempName = tmpOut.getName(); |
| |
| if (byteToCorrupt >= in.length()) { |
| throw new IllegalArgumentException("byteToCorrupt=" + byteToCorrupt + " but file \"" + out.getName() + "\" is only length=" + in.length()); |
| } |
| |
| tmpOut.copyBytes(in, byteToCorrupt); |
| // Flip the 0th bit: |
| tmpOut.writeByte((byte) (in.readByte() ^ 1)); |
| tmpOut.copyBytes(in, in.length()-byteToCorrupt-1); |
| } |
| |
| // Delete original and copy corrupt version back: |
| dir.deleteFile(out.getName()); |
| dir.copyFrom(dir, newTempName, out.getName(), IOContext.DEFAULT); |
| dir.deleteFile(newTempName); |
| } |
| |
| @Override |
| public long getFilePointer() { |
| return out.getFilePointer(); |
| } |
| |
| @Override |
| public long getChecksum() throws IOException { |
| return out.getChecksum() ^ 1; |
| } |
| |
| @Override |
| public String toString() { |
| return "CorruptingIndexOutput(" + out + ")"; |
| } |
| |
| @Override |
| public void writeByte(byte b) throws IOException { |
| out.writeByte(b); |
| } |
| |
| @Override |
| public void writeBytes(byte[] b, int offset, int length) throws IOException { |
| for(int i=0;i<length;i++) { |
| writeByte(b[offset+i]); |
| } |
| } |
| } |