Kouros
Kouros

Reputation: 361

Linux C read a directory

Hello I want to read from and write to a directory just like reading from and writing to files. I always use the open, read, write and close functions, which means I use descriptors. But doing this on a directory doesn't work, the open call works, but read returns -1 and errno is EISDIR. Am I forced to use streams to read a directory?

Upvotes: 2

Views: 4401

Answers (3)

Hosein Rahnama
Hosein Rahnama

Reputation: 145

As @caf pointed out, you should use readdir for this purpose. Indeed, one uses a sequence of opendir, readdir, closedir for reading directory files. Futhermore, directories are read only and cannot be written. However, if you are curious about using getdents system call to read a directory, here is a minimal example without error checking. Here, the function read_dirent takes the file descriptor corresponding to your directory and returns a directory entry on each call. The directory name can be passed as a command line argument. The default behavior prints the content of the current working directory. You can read more about getdents in here or here.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>

#define BUF_SIZE 1024

typedef struct linux_dirent 
{
    long           d_ino;
    off_t          d_off;
    unsigned short d_reclen;
    char           d_name[];
} LinuxDirEnt;

LinuxDirEnt *read_dirent(int fd);

int main(int argc, char *argv[])
{
    int fd;
    LinuxDirEnt *d;

    fd = open(argc > 1 ? argv[1] : ".", O_RDONLY);

    printf("inode#  d_reclen  d_name\n");
    while(d = read_dirent(fd))
        printf("%8ld  %4d  %s\n", d->d_ino, d->d_reclen, d->d_name);   

    exit(EXIT_SUCCESS);
}

LinuxDirEnt *read_dirent(int fd)
{
    static int nread = 0;
    static int bpos = 0;
    static char buf[BUF_SIZE];
    static LinuxDirEnt *d;
    
    if (nread == 0)
    {
        nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
        if (nread == 0)
            return NULL;
        bpos = 0;
    }
    else
        bpos += d->d_reclen;
    d = (LinuxDirEnt *)(buf + bpos);
    nread -= d->d_reclen; 

    return d;
}

Upvotes: 1

caf
caf

Reputation: 239321

The read() and write() system calls cannot be used on directories. Instead, the getdents() / getdents64() system calls are used to read a directory. Directories cannot be directly written at all.

Futhermore, glibc does not provide a wrapper for the getdents() / getdents64() system calls - instead it provides the POSIX-conforming readdir() function, which is implemented using those system calls. Most programs should use readdir(), but it is possible to call the system calls directly using syscall().

Upvotes: 3

markmb
markmb

Reputation: 892

I found this code here in Stack Overflow (How can I get the list of files in a directory using C or C++?), that helped me a lot in understanding how it works:

#include <dirent.h>
#include <stdio.h>
#include <string.h>

int main(){
    DIR* dirFile = opendir( "." );
    struct dirent* hFile;
    if ( dirFile ) 
    {
    while (( hFile = readdir( dirFile )) != NULL ) 
    {
       if ( !strcmp( hFile->d_name, "."  )) continue;
       if ( !strcmp( hFile->d_name, ".." )) continue;

     // in linux hidden files all start with '.'
       if ( gIgnoreHidden && ( hFile->d_name[0] == '.' )) continue;

     // dirFile.name is the name of the file. Do whatever string comparison 
     // you want here. Something like:
        if ( strstr( hFile->d_name, ".c" ))
           printf( "found an .txt file: %s", hFile->d_name );
    } 
  closedir( dirFile );
 }
}

Upvotes: 1

Related Questions