尽管写入成功很多,但写入()失败的EBADF

时间:2014-09-08 10:37:43

标签: c linux file-io

希望你们能指出我明显的错误;

系统是嵌入式linux,代码在c。

我们在cgi脚本中有一些代码,它接受来自STAIN_FILENO上的(Boa)webserver(文件上载)的数据流,并将其写入临时文件(" /tmp/file.txt" ;)

以下代码是一个非常简化的代码段,但基本上显示了事物的顺序:

  1. 打开临时文件fd以写入
  2. 从STDIN获取N个字节
  3. 将N个字节写入临时文件
  4. 泡沫,冲洗,重复直至不再有字节(或错误)
  5. 预期的文件是一个二进制文件,大约20Mb,但是写入总是失败,大约1.4Mb,而errno 9(EBADF)表明文件描述符以某种方式关闭。

    有足够的磁盘空间,Web服务器不限制POST文件大小或截断数据。

    代码:

    char buffer[1024];
    mode_t fd_mode=S_IRWXU;
    if ((targ_fd = open("/tmp/file.txt", O_WRONLY | O_CREAT, fd_mode)) == -1)
    {
        OUTPUT("Error opening file\n");
        return FAIL;
    }
    
    while(total < maxlen)
    {
        count = read(STDIN_FILENO, buffer, 1024);
        if(count > 0)
        {
           if(write(targ_fd, buffer, count) < 0)
            {
                perror("Failed to write to file");
                close(targ_fd);
                return FAIL;
            }
           total += count;
        }
        else
        {
            return FAIL;
            // Or success if EOF, code omitted for clarity
        }
    }
    

    使用较小的文件(但足够大,需要多次写入),它可以很好地工作。

    任何想法都非常感谢!

    已编辑添加:已更新代码,并且:

    我们抓住read()返回0但是为了清楚起见我省略了它。 程序是单线程的,在最初从第一个read()数据中修剪MIME头之后,它只是读入缓冲区&amp;然后将缓冲区写入临时文件,直到它达到maxlen或输入结束。

    附加说明:写入最初成功,使用正确的内容创建文件,直到失败的大小。

    通过进一步测试更新 以下是有用的意见/建议:

    • count是一个int,在那里签名没问题。

    • 我忘了检查write()的返回值,在失败之前的最后一次write()没有设法写入请求的完整字节数。更改open()以添加标志O_SYNC没有任何区别。

    • 我添加了fstat()并在open()和失败时打印调试:

    代码:

    printf("File type:                ");
    switch (filestat.st_mode & S_IFMT) {
    case S_IFBLK:  printf("block device\n");            break;
    case S_IFCHR:  printf("character device\n");        break;
    case S_IFDIR:  printf("directory\n");               break;
    case S_IFIFO:  printf("FIFO/pipe\n");               break;
    case S_IFLNK:  printf("symlink\n");                 break;
    case S_IFREG:  printf("regular file\n");            break;
    case S_IFSOCK: printf("socket\n");                  break;
    default:       printf("unknown?\n");                break;
    }
    
    printf("I-node number:            %ld\n", (long) filestat.st_ino);
    printf("Mode:                     %lo (octal)\n",
        (unsigned long) filestat.st_mode);
    printf("Link count:               %ld\n", (long) filestat.st_nlink);
    printf("Ownership:                UID=%ld   GID=%ld\n",
        (long) filestat.st_uid, (long) filestat.st_gid);
    printf("Preferred I/O block size: %ld bytes\n",
        (long) filestat.st_blksize);
    printf("File size:                %lld bytes\n",
        (long long) filestat.st_size);
    printf("Blocks allocated:         %lld\n",
        (long long) filestat.st_blocks);
    printf("Last status change:       %s", ctime(&filestat.st_ctime));
    printf("Last file access:         %s", ctime(&filestat.st_atime));
    printf("Last file modification:   %s", ctime(&filestat.st_mtime));
    

    结果是打开

    Fd = 5
    File type:                regular file
    I-node number:            486
    Mode:                     100600 (octal)
    Link count:               1
    Ownership:                UID=0   GID=0
    Preferred I/O block size: 4096 bytes
    File size:                0 bytes
    Blocks allocated:         0
    Last status change:       Thu Jan  1 00:00:51 1970
    Last file access:         Thu Jan  1 00:00:51 1970
    Last file modification:   Thu Jan  1 00:00:51 1970
    

    错误结果:

    Fd = 5
    Failed to write to the pipe (errno = 9), managed 273 / 4096 bytes
    File type:                regular file
    I-node number:            486
    Mode:                     100600 (octal)
    Link count:               1
    Ownership:                UID=0   GID=0
    Preferred I/O block size: 4096 bytes
    File size:                1499136 bytes
    Blocks allocated:         2944
    Last status change:       Thu Jan  1 00:00:51 1970
    Last file access:         Thu Jan  1 00:00:51 1970
    Last file modification:   Thu Jan  1 00:00:51 1970
    

    是的,系统时钟未设置。

    更多信息: 失败后:

    root@IPNC:~# df -i /tmp
    Filesystem Inodes Used Available Use% Mounted on
    none 5664 7 5657 0% /tmp
    root@IPNC:~# df /tmp
    Filesystem 1K-blocks Used Available Use% Mounted on
    none 25600 1508 24092 6% /tmp
    

    根据要求提供更多信息:

    继续进行的MIME修剪只是跳过第一个到达的数据,直到&#34; \ r \ n \ r \ n \ n&#34;在数据之前,所以第一次写入开始从缓冲区中途读取。我已经检查过,这是从正确的点开始的。在正确的点结束,并且使用较小的测试文件(~50k),正确接收整个文件。写得没有错误,过度运行或欠载。

    进一步测试的结果:

    嗯,它还没有修好。故障点随着不同的输入数据(文件大小和类型)而移动,但似乎没有跳过任何特定模式或数据(EG:一个文件在大块0xFF和#39中间失败) ; S)

    我尝试使用fcntl()来锁定fd,使用dup()并处理新的fd,以防另一个以某种方式在外部关闭,两者都没有任何区别。

    我将尝试插入虚拟数据并查看是否可以成功写入 filesize 字节的虚拟数据。

    **终于接近尾声...... **

    看起来好像Boa以奇怪的方式处理HTTP POST请求,将它们写入临时文件,将其复制到目标CGI脚本的STDIN。如果/ tmp中有足够的空间用于两个副本,那就没问题了,但是没有(我们已经嵌入)了。还有一些奇怪的地方:它创建的文件永远不会出现在文件列表中,而写入失败并不表示“#34;空间不足”。有些奇怪的事情肯定会发生,但是现在我认为这不是write()的失败,而是父代码中的一些更大的问题。

1 个答案:

答案 0 :(得分:0)

嗯,我想我应该关闭这个...

这样的答案有点复杂。

最初我说&#34;我们有足够的磁盘空间&#34;我们这样做,直到Boa决定缓冲/ tmp文件夹中的整个POST数据(~20Mb)(总可用空间~25Mb),然后将文件数据写入/ tmp非常棘手,并解释为什么write()稍后失败,文件越小(大约25Mb减去文件大小),大概如果文件​​是12.5Mb就会成功。

无论如何,通过奇数返回码(EBADF)使鹅追逐变得更加狂野,而不是任何类型的空间指示。

令人失望的结果是我们必须通过其他方式(tftp,wget)直接将文件上传到设备,或者使用上传页面中的脚本进行分割将文件分成块并一次上传一个块。

相关问题