Check if the fstream file is a file or directory

I am using C ++ fstream to read the configuration file.

#include <fstream> std::ifstream my_file(my_filename); 

Right now, if I pass the directory path, it silently ignores this. For example. my_file.good() returns true even if my_filename is a directory. Since this is an unintentional input for my program, I like to check it and throw an exception.

How to verify that a fstream just opened is a regular file, directory, or stream?

I can't seem to find a way:

  • get the file descriptor from the given ifstream.
  • use another mechanism to find this information in ifstream.

In the discussion of the forum, it was suggested that this is impossible because it depends on the OS and therefore can never be part of the standard C ++ standard.

The only alternative I can think of is to rewrite my code to completely get rid of ifstream and resort to the file descriptor C method ( *fp ), as well as fstat() :

 #include <stdio.h> #include <sys/stat.h> FILE *fp = fopen(my_filename.c_str(), "r"); // skip code to check if fp is not NULL, and if fstat() returns != -1 struct stat fileInfo; fstat(fileno(fp), &fileInfo); if (!S_ISREG(fileInfo.st_mode)) { fclose(fp); throw std::invalid_argument(std::string("Not a regular file ") + my_filename); } 

I prefer fstream. Hence my question.

+7
c ++ c ++ 11 fstream ifstream
source share
3 answers
 void assertGoodFile(const char* fileName) { ifstream fileOrDir(fileName); //This will set the fail bit if fileName is a directory (or do nothing if it is already set fileOrDir.seekg(0, ios::end); if( !fileOrDir.good()) { throw BadFile(); }; } 
+3
source share

There are various approaches to solving this problem:

  • Ignore him. Seriously, if the contents of the directory go as a valid configuration, I will be surprised. If not, parsing will still fail, so you do not run the risk of importing bad data. In addition, you do not prevent users from providing a channel or something similar that is not a file.
  • Check the path before it opens. You can use stat() or directly use Boost.Filesystem or some similar library. I am not 100% sure if something like this was added in C ++ 11. Please note that this creates a race condition, although, after checking, but before opening, some attacker can switch the file using the directory.
  • Typically, there are ways to get a low-level descriptor from fstream , in your case, probably FILE* . There are also ways to create an iostream (not necessarily fstream !) From FILE* . They are always extensions for the implementation, so you will need the #ifdef magic to adapt your code to the stdlibrary implementation used. I would dare to rely on their presence, even if you cannot create streambuf tof for FILE* , if you need to port some kind of obscure system that does not provide an easier way.
+1
source share

It is believed that this is difficult due to the OS's dependency on I / O operations.

I tried several methods in OS X 10.10.2, Linux 2.6.32, and FreeBSD 8.2-RELEASE (the last two are somewhat older OSs, I used several older VirtualBox virtual machines).

  • I haven't found a stupid method yet. If you really want to check, use stat() in the path or the old-fashioned C open() with fstat() .
  • seekg(0, std::ios::beg); method seekg(0, std::ios::beg); proposed by PSkocik did not work for me.
  • For fstreams, the best way is to simply open and read from the file and wait for the error.
  • Both bytebit and failbit must be installed, especially on OS X, to get all the necessary exceptions. For example. my_file.exceptions(std::ios::failbit | std::ios::badbit);
  • It also raises the end of file exception (EOF) after reading a regular file, which requires the code to ignore these ordinary exceptions.
  • my_file.eof() can also be set to more serious errors, so this is poor EOF state control.
  • errno is the best indicator: if an exception occurs, but errno is still 0, this is most likely an EOF condition.
  • It is not always so. On FreeBSD 8.2, opening a directory returns a binary gobbledygook, while no exceptions are thrown.

This is an implementation that seems to handle this somewhat reasonably on the three platforms I tested.

 #include < iostream> #include < fstream> #include < cerrno> #include < cstring> int main(int argc, char *argv[]) { for (int i = 1; i < argc; i++) { std::ifstream my_file; try { // Ensure that my_file throws an exception if the fail or bad bit is set. my_file.exceptions(std::ios::failbit | std::ios::badbit); std::cout << "Read file '" << argv[i] << "'" << std::endl; my_file.open(argv[i]); my_file.seekg(0, std::ios::end); } catch (std::ios_base::failure& err) { std::cerr << " Exception during open(): " << argv[i] << ": " << strerror(errno) << std::endl; continue; } try { errno = 0; // reset errno before I/O operation. std::string line; while (std::getline(my_file, line)) { std::cout << " read line" << std::endl; // ... } } catch (std::ios_base::failure& err) { if (errno == 0) { std::cerr << " Exception during read(), but errono is 0. No real error." << std::endl; continue; // exception is likely raised due to EOF, no real error. } std::cerr << " Exception during read(): " << argv[i] << ": " << strerror(errno) << std::endl; } } } 
0
source share

All Articles