5

I have a program reading from a file "foo" using C++ using:

pFile = fopen ("foo" , "r");

I want it to stop executing the rest of the function if the file is a named pipe. Is there a way to check if the file is a named pipe before opening it?

I found the exact same question using python: Check if file is a named pipe (fifo) in python? Can I do something similar in C++?

Community
  • 1
  • 1
crypton480
  • 155
  • 1
  • 4

3 Answers3

8

From man 2 stat:

int fstat(int filedes, struct stat *buf);

...The following POSIX macros are defined to check the file type using the st_mode field:

         S_ISFIFO(m) FIFO (named pipe)?

So struct stat st; ... !fstat(fileno(pFile, &st) && S_ISFIFO(st.st_mode) should work.

Edit: See also SzG's excellent answer, and Brian's comment to it.

Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
  • 1
    There is also [`S_ISFIFO(st.st_mode)`](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html#tag_13_62), which is a macro that tests for a pipe or FIFO special file. It is not clear that you can distinguish a pipe from a FIFO. – Jonathan Leffler Jan 30 '14 at 22:51
2

It might be a bit too late to stop the execution AFTER fopen(). It's because the open() syscall will block until someone has opened the FIFO for writing. Instead, use the stat() syscall (on Unix/Linux) before fopen() to find out.

SzG
  • 12,333
  • 4
  • 28
  • 41
  • 6
    Using `stat` then `fopen` can lead to a TOCTTOU vulnerability. The best solution if this is a security issue is to `open` non-blocking then `fstat` and `fdopen`. – Brian Bi Jan 30 '14 at 22:42
  • 2
    I thought a non-blocking `open()` means that the subsequent `read()` calls are non-blocking. But looking up the man pages confirmed that the `open()` itself becomes non-blocking too. – SzG Jan 30 '14 at 22:54
0

In modern C++ there is also filesystem library, it is available since C++17 #include <experimental/filesystem>, in C++14 it was experimental #include <experimental/filesystem>

So you can you use is_fifo() now. filesystem::path class is constructible from std::string, which is construcible from const char*, so filesystem::is_fifo("/path/to/file") will work as expected. But this version can throw an exception, so bool is_fifo( const std::filesystem::path& p, std::error_code& ec ) noexcept; is your choice.

#if __cplusplus >= 201703L
  #include <filesystem>
  namespace filesystem = std::filesystem;
#else
  #include <experimental/filesystem>
  namespace filesystem = std::experimental::filesystem;
#endif

bool is_fifo(const char *path)
{
    std::error_code ec;
    bool res = filesystem::is_fifo(path, ec);
    if (ec.value() != 0)
        std::cerr << ec.message() << std::endl;
    return res;
}

And don't forget this notice:

GNU implementation prior to 9.1 requires linking with -lstdc++fs and LLVM implementation prior to LLVM 9.0 requires linking with -lc++fs.

artaxerx
  • 244
  • 1
  • 3
  • 11