2

I just discovered that a FILE* can not only refer to a regular file, but also to a directory. If the latter is the case, fread will fail with errno set to 21 (Is a directory).

Minimal repro can be tested here

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>

int main() {
    char const* sz = ".";

    int fd = open(sz, O_RDONLY | O_NOFOLLOW); // all cleanup omitted for brevity
    FILE* file = fdopen(fd, "rb");
    // I would like to test in this line if it is a directory

    char buffer[21];
    int const n = fread(buffer, 1, 20, file);
    if (0 < n) {
        buffer[n] = 0;
        printf(buffer);
    } else {
        printf("Error %d", errno); // 21 = Is a directory
    }
}

What is the proper way to detect early that my FILE* is referring to directory without trying to read from it?

EDIT to repel the duplicate flags: I want to test on the FILE*, not the filename. Testing on filename only and then opening it later is a race condition.

Tobi
  • 2,591
  • 15
  • 34

3 Answers3

1

Assuming you are on a POSIX-based system, use stat() (if you wish to use the filename in sz before the call to open()) or fstat() (if you wish to use the descriptor fd after calling open()) to get a file status structure from the OS. The member of the structure named st_mode can be used with the POSIX API S_ISDIR(st_mode) to see if the file is a directory.

For more information, see: http://man7.org/linux/man-pages/man2/stat.2.html

JohnH
  • 2,713
  • 12
  • 21
1

Assuming a POSIX-like environment, if you have just the file stream (FILE *fp), then you are probably reduced to using fileno() and fstat():

#include <sys/stat.h>


struct stat sb;
if (fstat(fileno(fp), &sb) != 0)
    …oops…
if (S_ISDIR(sb.st_mode))
    …it is a directory…
else
    …it is not a directory…
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • So I have to check one line earlier (on the file descriptor rather than the file stream). Just out of curiosity: Is there anything useful that can be done with a `FILE*` directory, or is it a design oversight that the `fdopen` succeeds in this case? – Tobi Dec 13 '18 at 16:57
  • There are sound reasons to allow you to open a directory for reading — look at [`fchdir()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html), [`fstatat()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html), [`fchmodat()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html), etc. It isn't a design oversight. Frankly, testing the result of the stream failing is pretty reliable, too. – Jonathan Leffler Dec 13 '18 at 17:01
  • I see that `open`ing a directory makes sense. But should why should we be able to `fdopen` it? – Tobi Dec 13 '18 at 17:04
  • Because it is a valid file descriptor. – Jonathan Leffler Dec 13 '18 at 17:04
0

Checking The fcntl.h man page:

header shall define the following symbolic constants as file creation flags for use in the oflag value to open() and openat(). The values shall be bitwise-distinct and shall be suitable for use in #if preprocessing directives.

And the flag :

O_DIRECTORY Fail if not a directory.