Heinrich Heinrich - 1 month ago 9
C Question

plain C: opening a directory with fopen()

I have a program which opens a file and checks its length.

FILE* fd = fopen(argv[1], "rb");
fseek(fd, 0, SEEK_END);
size_t flen = ftell(fd);
if (flen == ((size_t)-1)) {
printf("%s is a directory.\n", argv[1]);

Now, at least under Linux,
returns a valid file descriptor when opening a directory. This results in the seek operation returning
(or, as
is unsigned,
on a 64-bit system).

Unfortunately, the condition in the above code (
flen == ((size_t)-1)
) does not catch that case, neither does
flen == 0xFFFFFFFF
-Commands with
as format string show that both sides of the comparison should have the same value.

Why does the comparison operator behave in such a strange way, even when both sides are of the same type (
)? I am using gcc 4.8.1 as compiler.

Answer Source

Directories do not exist in the C99 standard (or the C2011 one). So by definition, fopen-ing a directory is either implementation specific or undefined behavior.

fopen(3) can fail (giving a NULL result). fseek(3) can also fail (by returning -1). And then you should preferably check errno(3) or use perror(3)

ftell is documented to return a long, and -1L on failure. On 64 bits Linux this is 0xffffffffffffffff.

You code should be instead

FILE* fd = fopen(argv[1], "rb");
if (!fd) 
  { perror(argv[1]); exit(EXIT_FAILURE); };
if (fseek(fd, 0, SEEK_END)<0) 
  { perror("fseek"); exit(EXIT_FAILURE); };
long flen = ftell(fd);
if (flen == -1L)
  { perror("ftell"); exit(EXIT_FAILURE); };

BTW, On Linux/Debian/Sid/AMD64 with libc-2.17 and 3.10.6 kernel, that codes runs ok when argv[1] is /tmp; surprizingly, flen is LONG_MAX i.e. 0x7fffffffffffffff