What can cause close(2) to fail with EIO for a read-only file?

女生的网名这么多〃 提交于 2019-12-12 14:24:21

问题


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.


回答1:


I'm guessing the EIO comes from bad_file_flush in fs/bad_inode.c. It seems when the kernel has any failure accessing an inode, it transforms the open file description to a pseudo-open-file of with bad_inode_ops as its file ops. I can't find the code that does this for FAT-based filesystems, but perhaps there's some generic code.

As for the reason, it's probably something like attaching a USB cable and mounting the filesystem from an attached computer, removing the SD card, etc.




回答2:


In general, you should always anticipate IOExceptions when closing streams. The code is very straightforward, but see here for the cleanest example Java can afford:

https://stackoverflow.com/a/156520/1489860

However, in your specific case, I imagine an exception is being thrown because it appears you are changing the value of the InputStream in the unzipOrPassthrough(InputStream) method and then later trying to close it:

        if (entry == null) {
        is = new ByteArrayInputStream(baos.toByteArray());

When you later call close on the FileInputStream class, it probably freaks out because it is now a ByteArrayInputStream and no longer a FileInputStream.



来源:https://stackoverflow.com/questions/18708596/what-can-cause-close2-to-fail-with-eio-for-a-read-only-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!