I'm investigating a problem on Android where an IOException
is getting thrown because of a failure to close a file:
java.io.IOException: close failed: EIO (I/O error)
at libcore.io.IoUtils.close(IoUtils.java:41)
at java.io.FileInputStream.close(FileInputStream.java:121)
at com.adamrosenfield.wordswithcrosses.io.JPZIO.convertJPZPuzzle(JPZIO.java:191)
at com.adamrosenfield.wordswithcrosses.net.AbstractJPZDownloader.download(AbstractJPZDownloader.java:56)
at com.adamrosenfield.wordswithcrosses.net.AbstractJPZDownloader.download(AbstractJPZDownloader.java:41)
at com.adamrosenfield.wordswithcrosses.net.AbstractDownloader.download(AbstractDownloader.java:112)
at com.adamrosenfield.wordswithcrosses.net.AbstractDownloader.download(AbstractDownloader.java:108)
at com.adamrosenfield.wordswithcrosses.net.Downloaders.download(Downloaders.java:257)
at com.adamrosenfield.wordswithcrosses.BrowseActivity.internalDownload(BrowseActivity.java:702)
at com.adamrosenfield.wordswithcrosses.BrowseActivity.access$6(BrowseActivity.java:696)
at com.adamrosenfield.wordswithcrosses.BrowseActivity$7.run(BrowseActivity.java:691)
at java.lang.Thread.run(Thread.java:856)
Caused by: libcore.io.ErrnoException: close failed: EIO (I/O error)
at libcore.io.Posix.close(Native Method)
at libcore.io.BlockGuardOs.close(BlockGuardOs.java:75)
at libcore.io.IoUtils.close(IoUtils.java:38)
... 11 more
The relevant code:
public static void convertJPZPuzzle(File jpzFile, File destFile,
PuzzleMetadataSetter metadataSetter) throws IOException {
FileInputStream fis = new FileInputStream(jpzFile);
try {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(destFile));
try {
if (!convertJPZPuzzle(fis, dos, metadataSetter)) {
throw new IOException("Failed to convert JPZ file: " + jpzFile);
}
} finally {
dos.close();
}
} finally {
fis.close();
}
}
The full source is on GitHub.
The exception is being thrown from the line fis.close()
. From what I can tell from reading the Android sources, it looks like FileInputStream.close()
just calls down into close(2)
on the underlying file descriptor in native code.
The manual pages don't seem to specify what can cause an EIO
error, they just say things like "An I/O error occurred." or "If an I/O error occurred while reading from or writing to the file system during close()". The Mac OS X man pages say it can occur when "A previously-uncommitted write(2) encountered an input/output error." on those systems.
What exactly can cause close(2)
to fail with the error EIO
for a file descriptor which was only opened for reading, as in this case? Clearly it's not an uncommitted write(2)
. In the case of this particular file, it was downloaded using Android's DownloadManager service, which means there might be lingering threads and/or processes trying to access it simultaneously, but I can hardly see how that would affect trying to close it. Also, the file is just about to be deleted after this code runs (here), but unless Android has an undocumented time machine in it, future code ought not to have an affect here.
I'm interested specifically in the answer on Android and/or Linux, but a more general answer for other OSes would also be appreciated.