Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

plain C: opening a directory with fopen()

Tags:

c

types

linux

io

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.

like image 872
Heinrich supports Monica Avatar asked Aug 12 '13 17:08

Heinrich supports Monica


People also ask

Can fopen open a directory?

The fopen can't be used to create directories. This is because fopen function doesn't create or open folders, it only works with files. The above code creates a path to the file named 'filename'. The directory of the 'filename' is obtained using the 'dirname' function.

How do I open a directory in C #?

To access a directory, use the opendir() function. It's prototyped in the dirent. h header file as: DIR *opendir(const char *filename);

What is the return type of open () and fopen ()?

The key difference between the fopen() and the open() function in the Linux operating system is that the open() function is a low-level call, where the fopen() when called simply calls the open() function in the background and it returns a Filepointer directly.

What is DIR type in C?

The DIR data type represents a directory stream. You shouldn't ever allocate objects of the struct dirent or DIR data types, since the directory access functions do that for you. Instead, you refer to these objects using the pointers returned by the following functions. Directory streams are a high-level interface.


2 Answers

From http://pubs.opengroup.org/onlinepubs/7908799/xsh/fopen.html:

The fopen() function will fail if: 
[EISDIR] The named file is a directory and mode requires write access.

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.)

like image 128
user3689693 Avatar answered Sep 28 '22 02:09

user3689693


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

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 some FILE*) 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).

like image 44
Basile Starynkevitch Avatar answered Sep 28 '22 03:09

Basile Starynkevitch