安全使用std :: tmpnam

时间:2015-11-26 10:13:16

标签: c++ c++11 stl

所以我正在开发一个应用程序,它需要创建一个文件,写入它,调用另一个程序,该文件是一个输入,并删除该文件。

我寻找可能的解决方案,一个解决方案看起来像这样。

std::FILE* tmpf = std::tmpfile();
std::fputs("Hello, world", tmpf);

根据std::tmpfile的文档,如果文件是手动关闭,或者程序以自然方式退出,则文件将被删除。这看起来很好,但有一个例外。它看起来很乱(使用C I / O,而不是C ++流)。

另一种解决方案是使用std::tmpnam,它将生成唯一的文件名。

std::string file_name = std::tmpname(NULL);
// (*)
std::fstream stream{file_name};
stream << "Hello World" << std::endl;

但是这个也存在问题。如果另一个程序创建了具有相同名称的文件,而我的程序在(*),我们将在同一个文件上进行操作,这当然是我想避免的。

C ++ STL(仍然)不支持文件系统操作,例如检查文件是否存在。我可以使用stat之类的东西来检查它,但是因为检查文件并创建它不是原子的,所以它不能解决任何问题。而且我没有找到原子操作的C ++ STL方法,它会:检查文件是否存在,如果没有,打开它,如果是,则失败。

所以,我的问题是,解决这个问题的正确方法是什么?我错过了什么吗?

3 个答案:

答案 0 :(得分:2)

这是我几乎没有凌乱的解决方案:

#include <cstdio>
#include <iostream>
#include <string>
#include <ext/stdio_filebuf.h> // libstdc++ specific

int main()
{
    __gnu_cxx::stdio_filebuf<char> tmpfile_buf{std::tmpfile(), std::ios::in | std::ios::out | std::ios::binary};
    std::iostream tmpstream{&tmpfile_buf};

    // write in stream
    tmpstream << "Hello World" << std::endl;
    tmpstream.seekg(0);

    // read from stream
    std::string str;
    std::getline(tmpstream, str);
    std::cout << str << std::endl;
}

Live example

使用

std::tmpfile(),并从具有底层FILE*的缓冲区构建流。这是一个GNU扩展,因此不可移植:(。

答案 1 :(得分:2)

您的规范“...创建一个文件,写入其中,使用该文件调用另一个程序是一个输入,然后删除该文件。”与tmpfile()不兼容。首先,没有(便携式)方法从tmpfile()获取FILE指针获取文件名,其次在POSIX平台上,tmpfile()通常会在从tmpfile()返回之前从目录中删除文件(如果你不熟悉POSIX文件系统语义,只要你的进程有一个打开的文件描述符,即使它从目录中删除后,该文件仍然存在,这意味着无法从文件系统访问它。)

鉴于此,您将不得不使用某种tmpname()类型方法,并找出一种方法来防止两个进程同时访问它(文件锁定,使用stat()检查链接计数等)。

或者更好的是,不要使用文件进行进程间通信。对于最简单的(?)情况,在父节点中创建一个管道,将它连接到子节点的标准输入。

答案 2 :(得分:0)

我可能会提出Boost解决方案,但我无法在此进行测试。

FILE*返回std::tmpfile(),可以获得文件描述符:

#include <cstdio>
FILE* const tmpfile = std::tmpfile();
const int fd = ::fileno(tmpfile);

在这个阶段,使用boost::iostreams::file_descriptor_source

的一切看起来都不错
#include <boost/iostreams/code_converter.hpp>
#include <boost/iostreams/maped_file.hpp>
file_descriptor_source tmpstream(fd, boost::iostreams::close_handle);