LUCENE-9423: Handle exc in NIOFSDirectory#openInput (#1658)

If we fail to get the size of a file in the constructor of 
NIOFSIndexInput, then we will leak a FileChannel opened in
NIOFSDirectory#openInput.
This commit is contained in:
Nhat Nguyen 2020-07-09 09:14:05 -04:00 committed by GitHub
parent 4ae976bdf0
commit 20ec57a4fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 1 deletions

View File

@ -25,6 +25,8 @@ import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future; // javadoc
import org.apache.lucene.util.IOUtils;
/**
* An {@link FSDirectory} implementation that uses java.nio's FileChannel's
* positional read, which allows multiple threads to read from the same file
@ -79,7 +81,16 @@ public class NIOFSDirectory extends FSDirectory {
ensureCanRead(name);
Path path = getDirectory().resolve(name);
FileChannel fc = FileChannel.open(path, StandardOpenOption.READ);
return new NIOFSIndexInput("NIOFSIndexInput(path=\"" + path + "\")", fc, context);
boolean success = false;
try {
final NIOFSIndexInput indexInput = new NIOFSIndexInput("NIOFSIndexInput(path=\"" + path + "\")", fc, context);
success = true;
return indexInput;
} finally {
if (success == false) {
IOUtils.closeWhileHandlingException(fc);
}
}
}
/**

View File

@ -18,7 +18,17 @@ package org.apache.lucene.store;
import java.io.IOException;
import java.net.URI;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystem;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.Set;
import org.apache.lucene.mockfile.FilterFileChannel;
import org.apache.lucene.mockfile.FilterPath;
import org.apache.lucene.mockfile.LeakFS;
/**
* Tests NIOFSDirectory
@ -29,4 +39,29 @@ public class TestNIOFSDirectory extends BaseDirectoryTestCase {
protected Directory getDirectory(Path path) throws IOException {
return new NIOFSDirectory(path);
}
public void testHandleExceptionInConstructor() throws Exception {
Path path = createTempDir().toRealPath();
final LeakFS leakFS = new LeakFS(path.getFileSystem()) {
@Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options,
FileAttribute<?>... attrs) throws IOException {
return new FilterFileChannel(super.newFileChannel(path, options, attrs)) {
@Override
public long size() throws IOException {
throw new IOException("simulated");
}
};
}
};
FileSystem fs = leakFS.getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(path, fs);
try (Directory dir = new NIOFSDirectory(wrapped)) {
try (IndexOutput out = dir.createOutput("test.bin", IOContext.DEFAULT)) {
out.writeString("hello");
}
final IOException error = expectThrows(IOException.class, () -> dir.openInput("test.bin", IOContext.DEFAULT));
assertEquals("simulated", error.getMessage());
}
}
}