Reputation: 13069
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
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
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
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