如何在C中检测文件写入错误?

时间:2012-07-04 04:03:43

标签: c linux

我有一个嵌入式环境,用户可以在其中插入或移除USB闪存驱动器。我想知道驱动器是否已被移除,或者当我尝试写入驱动器时是否存在其他问题。但是,Linux只是将信息保存在缓冲区中并返回,没有指示错误 我正在使用的计算机带有2.4.26内核和libc 2.3.2 我正以这种方式安装驱动器:
    i = mount(MEMORY_DEV_PATH, MEMORY_MNT_PATH, "vfat", MS_SYNCHRONOUS, NULL);
这有效:
50:/root # mount
/dev/scsi/host0/bus0/target0/lun0/part1 on /mem type vfat (rw,sync)
50:/root #

稍后,我尝试将文件复制到它:

int ifile, ofile;
ifile = open("/tmp/tmpmidi.mid", O_RDONLY);
if (ifile < 0)
{
    perror("open in");
    break;
}
ofile = open(current_file_name.c_str(), O_WRONLY | O_SYNC);
if (ofile < 0)
{
    perror("open out");
    break;
}
#define BUFSZ 256
char buffer[BUFSZ];
while (1)
{
    i = read(ifile, buffer, BUFSZ);
    if (i < 0)
    {
        perror("read");
        break;
    }
    j = write(ofile, buffer, i);
    if (j < 0)
    {
        perror("write");
        break;
    }
    if (i != j)
    {
        perror("Sizes wrong");
        break;
    }
    if (i < BUFSZ)
    {
        printf("Copy is finished, I hope\n");
        close(ifile);
        close(ofile);
        break;
    }
}

如果使用写保护的USB存储器执行此代码片段,则结果为

Copy is finished, I hope

来自控制台内核的一连串错误消息 我相信如果我简单地移除USB驱动器(没有卸下它)就会发生同样的事情 我也摆脱了devfs。我想出了如何让它自动安装驱动器(使用REGISTER事件)但是当我拔出内存时它似乎永远不会触发UNREGISTER。
如何在我的程序中确定是否已成功创建文件?

7月4日更新: 对我来说,不检查close()的结果是一种愚蠢的疏忽。不幸的是,文件可以无误地关闭。所以这没有帮助。那fsync()怎么样?这听起来是个好主意,但也没有发现错误 如果我有这样的事情,/ sys中可能会有一些有趣的信息。我相信直到2.6才会增加。?。 关于我的闪存驱动器质量的评论可能是合理的。这是早先的一个。事实上,写保护开关现在似乎非常罕见 我想我必须使用overkill选项:创建一个文件,卸载&amp;重新安装驱动器,并检查文件是否存在。如果这不能解决我的问题,那么事情就搞砸了!自己注意:确保您尝试创建的文件不在那里!
顺便说一下,这恰好是一个C ++程序。您可以通过.c_str()告诉我,为了简单起见,我打算编辑它。

5 个答案:

答案 0 :(得分:3)

如果您的应用程序想要将一个或多个文件保存到USB记忆棒中,并且让用户在小LED指示灯熄灭后立即拔出记忆棒,则需要

    在编写文件之前
  1. mount() USB记忆棒

    您不需要为此安装SYNC,这有助于使用劣质USB记忆棒

  2. 使用正确的代码保存文件

    您对C低级I / O的尝试非常不正确。特别是,允许​​任何read()write()返回短计数,1和所请求大小之间的任何正值,包括在内,而不指示任何类型的错误

  3. fsync() USB记忆棒上的文件

    验证它是否返回成功。如果是,那么您就知道数据已经击中了USB记忆棒。

  4. fsync()包含USB记忆棒上文件的目录

    验证它是否返回成功。如果确实如此,那么您就知道文件元数据已经点击了USB记忆棒,即使用户拉出USB记忆棒,该文件也应该可以访问。

  5. umount() USB记忆棒

    这将阻止,直到USB记忆棒已准备好断开连接,因此在umount()返回之前,您的应用程序看起来应保存到文件中。

    如果您执行了上述fsync(),则umount()应该非常直接。根据文件系统的不同,内核可能需要进行一些簿记,但在任何情况下都不应该花费很长时间。

  6. 其他任何事情都不可靠。您可以通过安装具有同步访问权限的VFAT分区来做出某些假设,但这基本上只是手工操作。

    如果您不想以root权限运行应用程序,则始终可以编写一个管理安装和卸载的简单特权服务。如果您怀疑在某些时候可能需要多个应用程序,我会非常热情地推荐,因为只有集中的安装程序/安装程序才能知道媒体何时准备就绪。 (如果另一个应用程序同时写入同一USB介质,它可以延迟返回“未安装”消息。顺便说一句,当你不需要同步安装USB介质时,这种方法很有效。)我个人会在/var/run/中使用unix域数据报套接字,可能是/var/run/mounter.socket,用于进程间通信。

    最后,如果您的Linux内核已正确配置并且已安装/sys/分区,则可以扫描所有/sys/block/sd?/目录中的可移动媒体:

    • /sys/block/sd?/removable将包含非零十进制数字符串
    • /sys/block/sd?/size包含以512字节为单位的设备大小,作为十进制数字字符串
    • /sys/block/sd?/device/vendor包含供应商名称字符串
    • /sys/block/sd?/device/model包含型号名称为字符串

    这些条目是硬件级别的,只要USB记忆棒连接并供电,就可以使用;无论是否安装都无关紧要。如果/当用户拿出USB记忆棒时,整个设备目录树将立即消失。

答案 1 :(得分:2)

如果要检测所有写入错误,则需要检查的不仅仅是write()的返回代码 - 您还必须调用fsync()(并检查返回值),并检查close()报告的错误。

答案 2 :(得分:1)

简短回答:问题在于操作系统和/或USB驱动器。 Linux的 将(有时?)愉快地安装USB驱动器即使可写 写保护开关已设置。当它实际上试图写入它 然而,驱动器拒绝,这或多或少作为一个处理 驱动器故障。 (我不确定为什么会发生这种情况。我的猜测就是这样 闪存驱动器未向操作系统报告其只读权限。这个 可能取决于您正在使用的闪存驱动器的品牌和型号。)

我可以在3.2.0-26上使用(非常旧的)USB密钥获得相同的行为 内核(特别是Ubuntu 12)。我没有遇到麻烦 写保护的USB密钥读写和'cp'如果我复制一个就不会抱怨 提交给它。该文件甚至可能出现在目录中(因为 缓冲)但实际上没有任何东西写入它。我确实得到了 系统日志中有很多错误消息。

如果我是你,我会尝试实际写入闪存驱动器并确保 在假设驱动器实际可写之前它成功了。 具体来说,我:

  1. 将驱动器安装为可写。

  2. 使用唯一名称在驱动器上创建一个新文件。

  3. 卸载驱动器以清空缓冲区。

  4. 重新安装驱动器。

  5. 检查新文件是否仍在,并包含您写入的数据。

  6. 删除测试文件。

  7. 我没有充分了解硬件和驱动程序状态告诉你 如果有办法从API检测不可写的驱动器 - 可能会 是的,但我不知道。但即使有,这种情况也会发现 USB驱动器运行不正常。

    <强>更新

    我做了一些研究,结果发现如果设置了写保护开关,文件句柄上的fsync()将会失败。因此,我撤回了上述建议。相反,这是我的测试程序:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    
    int
    main(int argc, char *argv[]) {
        char *path;
        int f;
        size_t stat;
        const char *message = "Hello, world.\n";
    
        if (argc != 2) {
            printf("Need filename\n");
            exit(1);
        }
    
        path = argv[1];
    
        f = open(path, O_CREAT | O_WRONLY | O_SYNC, S_IRWXU);
        if (f < 0) {
            perror("open out");
            exit(1);
        }/* if */
    
        stat = write(f, message, strlen(message));
        if (stat < 0) {
            perror("write");
            exit(1);
        }/* if */
    
        stat = fsync(f);
        if (stat) {
            perror("fsync");
            exit(1);
        }
    
        stat = close(f);
        if (stat) {
            perror("close");
            exit(1);
        }/* if */
    
        printf("(Apparently) successfully wrote '%s'\n", path);
        return 0;
    }
    

    如果设备不可写,则应该在fsync()调用时失败。

答案 3 :(得分:0)

这是POSIX的做事方式。如果write的返回值为-1,您肯定知道某些事情发生了可怕的错误。但是,如果write返回0,则某些内容也可能出错。检查errno变量,看它是否与此处显示的预定义写入错误之一匹配:http://www.kernel.org/doc/man-pages/online/pages/man2/write.2.html

答案 4 :(得分:0)

如果找不到解决方案,可以试试这个丑陋的黑客攻击。在写入和关闭输出文件之后,您只需尝试在读取模式下打开它并检查它是否具有正确的大小。如果您确实希望确保它具有正确的内容,则可以验证它与您刚刚编写的文件具有相同的校验和。这假设操作系统将直接从USB驱动器读取文件,而不是某种缓存。