【问题标题】:What can cause close(2) to fail with EIO for a read-only file?对于只读文件,什么会导致 close(2) 因 EIO 失败?
【发布时间】:2013-09-13 13:17:27
【问题描述】:

我正在调查 Android 上的一个问题,即由于无法关闭文件而抛出 IOException:

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

相关代码:

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();
    }
}

完整来源是on GitHub。

从fis.close() 行抛出异常。从阅读 Android 源代码中可以看出,FileInputStream.close() 似乎只是在本机代码的底层文件描述符上调用了close(2)。

手册页似乎没有说明导致EIO 错误的原因,他们只是说“发生了 I/O 错误”之类的内容。或“如果在 close() 期间读取或写入文件系统时发生 I/O 错误”。 Mac OS X 手册页说,当“以前未提交的写入(2)遇到输入/输出错误”时,可能会发生这种情况。在那些系统上。

究竟是什么导致close(2) 因文件描述符错误EIO 而失败,而该文件描述符只为读取而打开,就像在这种情况下一样?显然这不是一个未提交的write(2)。在这个特定文件的情况下,它是使用 Android 的 DownloadManager 服务下载的,这意味着可能存在试图同时访问它的延迟线程和/或进程,但我几乎看不出这会如何影响尝试关闭它。另外,这段代码运行后文件即将被删除(here),但除非 Android 中有一个未记录的时间机器,否则以后的代码应该不会在这里产生影响。

我对 Android 和/或 Linux 上的答案特别感兴趣,但对于其他操作系统的更一般的答案也将不胜感激。

【问题讨论】:

  • 我不能走得太远,因为我不知道c 或posix 系统。相关问答here。 libcore.io here 的源代码和 close 的 native 实现(用于 fd)here。
  • 在最后一个链接中,您必须在第 429 行找到 close() 的实现。
  • @SotiriosDelimanolis:是的,我看到了这个问题——讨论了EIO 错误之后文件描述符的状态;我对EIO 的原因 感兴趣,而不是在错误发生后该怎么做。 close()的实现是Linux内核内部的系统调用。
  • 这似乎与坏 inode 相关,但挖掘内核代码所用的时间比我想象的要长...
  • 这里也一样。除了奇异的文件系统之外,我看不到任何 close 可以返回 EIO 的方式。有问题的文件是什么文件系统类型?

标签: java android c linux posix


【解决方案1】:

一般来说,您应该在关闭流时始终预料到 IOExceptions。代码非常简单,但请参阅此处以了解 Java 所能提供的最简洁的示例:

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

但是,在您的特定情况下,我想会引发异常,因为您似乎正在更改 unzipOrPassthrough(InputStream) 方法中 InputStream 的值,然后尝试关闭它:

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

当您稍后在 FileInputStream 类上调用 close 时,它​​可能会吓坏,因为它现在是 ByteArrayInputStream 而不再是 FileInputStream。

【讨论】:

  • 这甚至没有意义。如果它现在是一个ByteArrayInputStream 对象, 他不能 '稍后调用关闭FileInputStream 类'。他正在关闭ByteArrayInputStream。时期。关于“现在是ByteArrayInputStream 而不再是FileInputStream”的部分是无稽之谈。引用的类型为InputStream. 对象的引用无法记住它们指向的对象类型。
  • unzipOrPassthrough() 从传入的输入流中读取,但不会关闭它。它确实重新分配了本地参数is,但这对传入的原始流没有影响。
【解决方案2】:

我猜EIO 来自fs/bad_inode.c 中的bad_file_flush。似乎当内核在访问 inode 时出现任何故障时,它会将打开的文件描述转换为以 bad_inode_ops 作为其文件操作的伪打开文件。我找不到为基于 FAT 的文件系统执行此操作的代码,但也许有一些通用代码。

至于原因,可能是连接 USB 电缆并从连接的计算机安装文件系统、移除 SD 卡等。

【讨论】:

  • 由于不幸的是我无法访问我用户的设备,因此我无法确定到底发生了什么,但在那之前,这似乎是最有可能的解释。在内核源码中摸索了一会,似乎sys_close()只能返回0、-EINTR、-EBADF,或者文件系统的flush()实现的返回值,而vfat没有实现flush().
猜你喜欢
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多