简体   繁体   English

安全使用std :: tmpnam

[英]Safe use of std::tmpnam

so I am developing an application, which requires to create a file, write into it, call another program with that file is an input, and delete the file. 因此,我正在开发一个应用程序,它需要创建一个文件,写入其中,使用该文件作为输入来调用另一个程序,然后删除该文件。

I looked for possible solution and one solution looks like this. 我寻找了可能的解决方案,一个解决方案看起来像这样。

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

According to the documentation of std::tmpfile , if either the file is closed manually, or the program exits in natural way, the file will be deleted. 根据std::tmpfile的文档,如果手动关闭了文件,或者程序以自然方式退出,则该文件将被删除。 This looks like good solution with one exception. 这看起来是个好的解决方案,但有一个例外。 It looks messy (using CI/O, instead of C++ streams). 看起来很乱(使用CI / O而不是C ++流)。

Another solution would be to use std::tmpnam , which will generate unique file name. 另一个解决方案是使用std::tmpnam ,它将生成唯一的文件名。

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

But there is a problem with this one too. 但是这个也有问题。 If another program creates file with the same name, while my program is in (*) , we will both be doing operations on same file, which is certainly something I'd like to avoid. 如果另一个程序创建的文件具有相同的名称,而我的程序位于(*) ,则我们都将对同一文件执行操作,这当然是我要避免的事情。

C++ STL (still) does not support file system operations, like checking if file exists. C ++ STL(仍然)不支持文件系统操作,例如检查文件是否存在。 I could use something like stat to check that, but since checking for file and creating it is not atomic, it would not solve anything. 我可以使用stat东西进行检查,但是由于检查文件并创建它不是原子的,因此它无法解决任何问题。 And I did not find C++ STL method of atomic operation, which would: Check if file exists, if not, open it, if yes, fail. 而且我没有找到原子操作的C ++ STL方法,该方法将:检查文件是否存在,如果不存在,则将其打开,如果是,则失败。

So, my question is, what is the right way of solving this problem? 所以,我的问题是,解决这个问题的正确方法是什么? Did I miss something? 我错过了什么?

Here is my almost not messy solution: 这是我几乎没有混乱的解决方案:

#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() is used and a stream is build from a buffer having an underlying FILE* . std::tmpfile()并从具有基础FILE*的缓冲区构建流。 This is a GNU extension and thus not portable :(. 这是GNU扩展,因此不可移植:(。

Your specification "...create a file, write into it, call another program with that file is an input, and delete the file." 您的规范“ ...创建文件,写入文件,使用该文件作为输入来调用另一个程序,然后删除该文件。” is incompatible with tmpfile(). 与tmpfile()不兼容。 For one thing, there is no (portable) way to get the file name from the FILE pointer you get from tmpfile(), secondly on POSIX platforms tmpfile() will typically delete the file from the directory before returning from tmpfile() (if you're unfamiliar with POSIX filesystem semantics, the file exists as long as your process has an open file descriptor to it even after it's deleted from the directory, meaning there is no way to access it from the filesystem). 一方面,没有(便携式)方法可以从您从tmpfile()获得的FILE指针中获取文件名,其次,在POSIX平台上,tmpfile()通常会从目录中删除文件,然后再从tmpfile()返回(如果您不熟悉POSIX文件系统的语义,只要您的进程具有打开的文件描述符(即使从目录中删除该文件),该文件就存在,这意味着无法从文件系统访问它。

Given that, you'll have to use some kind of tmpname() type approach, and figure out a way to prevent two processes from simultaneously accessing it (file locks, check link count with stat(), or such). 鉴于此,您将必须使用某种tmpname()类型的方法,并找出一种防止两个进程同时访问它的方法(文件锁,使用stat()检查链接数等)。

Or better yet, don't use files for interprocess communication. 或者更好的是,不要使用文件进行进程间通信。 For the simplest possible(?) case, create a pipe in the parent, connect it to stdin of the child. 对于最简单的情况(?),请在父级中创建一个管道,然后将其连接到子级的stdin。

I may have a Boost solution to suggest, but I can't test it here. 我可能有建议的Boost解决方案,但是我不能在这里进行测试。

With a FILE* returned from std::tmpfile() , one can get a file descriptor: 使用从std::tmpfile()返回的FILE* ,可以获取文件描述符:

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

and at this stage, everything looks good to use boost::iostreams::file_descriptor_source : 在这个阶段,使用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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM