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]);
fclose(fd);
exit(1);
}
Now, at least under Linux, fopen()
returns a valid file descriptor when opening a directory. This results in the seek operation returning -1
(or, as size_t
is unsigned, 0xFFFFFFFFFFFFFFFF
=264-1 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
(EDIT: should be 0xFFFFFFFFFFFFFFFF
). printf()
-Commands with %x
ord %d
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 (size_t
)? I am using gcc 4.8.1 as compiler.
From http://pubs.opengroup.org/onlinepubs/7908799/xsh/fopen.html:
At least on Linux, if you try to
fopen("dirname", "wb")
you get an EISDIR error. I also tried with a directory with d-------- access rights, and I still get EISDIR (and not EACCES.)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 along
, and-1L
on failure. On 64 bits Linux this is0xffffffffffffffff
.You code should be instead
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
isLONG_MAX
i.e.0x7fffffffffffffff
BTW, on Linux, directories are a special case of files. Use stat(2) on a file path (and
fstat
on a file descriptor, perhaps obtained with fileno(3) from someFILE*
) to know more meta data about some file, including its "type" (thru its mode). You want opendir(3), readdir(3) & closedir(3) to operate on directory contents. See also inode(7).