cschol
cschol

Reputation: 13069

Can I manipulate a directory stream obtained with opendir()?

The function opendir() returns a pointer to a directory stream DIR *, which apparently is an opaque data type. The implementation is hidden.

The libc manual states that you should not allocate a DIR object yourself and let the directory functions handle the allocation.

Is there any way to manipulate a directory stream after obtaining it with opendir() and before passing it to, for example, readdir()?

I basically want to overload opendir() with LD_PRELOAD to return a manipulated directory stream, which is used by readdir().

Upvotes: 1

Views: 1995

Answers (3)

Bartosz Moczulski
Bartosz Moczulski

Reputation: 1239

If I get you right you want to manipulate the directory entries, e.g. change file names or add dummy entries. You can do it like that.

Overload opendir(), within it really open the directory with "real" opendir(), immediately read all directory entries, with "real" readdir(), modify what's necessary, store the modified version in a global variable and return unmodified DIR *. Then in the overloaded readdir() you treat passed DIR * as your own opaque value (e.g. key in a map) and simply sequentially return previously prepared entries.

Here's a nasty proof of concept (nasty because I skipped the boring part like error checking, resource closing, memory freeing, thread safety, etc.):

opendir_wrap.cpp -> opendir_wrap.so:

#include <sys/types.h>
#include <dirent.h>
#include <dlfcn.h>
#include <stdio.h>
#include <map>
#include <list>

extern "C" {

static std::map<DIR *, std::list<struct dirent*> > MAP;

typedef DIR *(*OPEN_T)(const char *name);
typedef struct dirent *(*READ_T)(DIR *dirp);
static OPEN_T real_opendir = NULL;
static READ_T real_readdir = NULL;

DIR *opendir(const char *name)
{
    void *handle = dlopen("/lib/libc.so.6", RTLD_LAZY);
    if (!real_opendir) real_opendir = (OPEN_T) dlsym(handle, "opendir");
    if (!real_readdir) real_readdir = (READ_T) dlsym(handle, "readdir");

    DIR *dirp = real_opendir(name);
    struct dirent *entry = NULL;
    while (entry = real_readdir(dirp))
    {
        MAP[dirp].push_back(entry);
    }
    MAP[dirp].push_back(NULL);

    // your modifications here
    struct dirent *joke = new struct dirent;
    sprintf(joke->d_name, "JOKE!");
    MAP[dirp].push_front(joke);

    return dirp;
}

struct dirent *readdir(DIR *dirp)
{
    struct dirent *entry = MAP[dirp].front();
    MAP[dirp].pop_front();
    return entry;
}

} // extern "C"

opedir_use.c -> opendir_use:

#include <sys/types.h>
#include <dirent.h>
#include <dlfcn.h>
#include <stdio.h>

int main()
{
    struct dirent *entry = NULL;
    DIR *dirp = opendir(".");
    printf("dirp = %p\n", dirp);
    while (entry = readdir(dirp))
    {
        printf("entry->d_name = %s\n", entry->d_name);
    }
}

Now compile:

$ gcc -fpic -shared -ldl -lstdc++ -o ./opendir_wrap.so ./opendir_wrap.cpp
$ gcc opendir_use.c -o opendir_use

Run normally:

$ ./opendir_use 
dirp = 0x9fd3008
entry->d_name = opendir_wrap.so
entry->d_name = opendir_use
entry->d_name = opendir_use.c
entry->d_name = opendir_wrap.cpp
entry->d_name = ..
entry->d_name = .

Run with the wrapper:

$ LD_PRELOAD=`pwd`/opendir_wrap.so ./opendir_use
dirp = 0x95374b8
entry->d_name = JOKE!
entry->d_name = opendir_wrap.so
entry->d_name = opendir_use
entry->d_name = opendir_use.c
entry->d_name = opendir_wrap.cpp
entry->d_name = ..
entry->d_name = .

Upvotes: 1

Dan D.
Dan D.

Reputation: 74675

It might help to look at what the LD_PRELOAD sortdir does as it sorts the directory entries before giving them to the program although what you might want to do might be something other than sorting.

sortdir replaces opendir, readdir, readdir64, and closedir and being only 197 lines of code, it would serve you well to look at it.

Upvotes: 3

JaredPar
JaredPar

Reputation: 755457

I don't believe this is possible, or at least not possible in any sort of portable fashion. As you said the DIR* type is an opaque pointer. The DIR file is defined in an implementation specific fashion in a file you don't have access to.

In order to manipulate the returned DIR value you'd have to create a struct of similar structure which contains the manipulated values. Implementations are free to vary the definition of DIR or change it without warning (it is opaque after all). So any implementation you added would be fragile at best.

Upvotes: 2

Related Questions