Могу ли я манипулировать потоком каталога, полученным с помощью opendir ()? - PullRequest
1 голос
/ 18 февраля 2012

Функция opendir() возвращает указатель на поток каталога DIR *, который, очевидно, является непрозрачным типом данных. Реализация скрыта.

В руководстве libc говорится, что вам не следует выделять объект DIR самостоятельно, и пусть функции каталога обрабатывают выделение.

Есть ли способ манипулировать потоком каталога после получения его с помощью opendir() и перед передачей, например, readdir()?

Я хочу перегрузить opendir() с помощью LD_PRELOAD, чтобы вернуть управляемый поток каталогов, который используется readdir().

Ответы [ 3 ]

3 голосов
/ 18 февраля 2012

Может быть полезно посмотреть, что делает LD_PRELOAD sortdir при сортировке записей каталога, прежде чем передать их программе, хотя вы можете захотеть сделать что-то иное, чем сортировка.

sortdir заменяет opendir, readdir, readdir64 и closedir и, имея всего 197 строк кода, вам будет полезно посмотреть на него.

2 голосов
/ 18 февраля 2012

Я не верю, что это возможно или, по крайней мере, невозможно каким-либо портативным способом. Как вы сказали, тип DIR* является непрозрачным указателем. Файл DIR определяется в зависимости от реализации в файле, к которому у вас нет доступа.

Чтобы манипулировать возвращенным значением DIR, вам нужно создать struct аналогичной структуры, которая содержит манипулируемые значения. Реализации могут изменять определение DIR или изменять его без предупреждения (в конце концов, оно непрозрачно). Поэтому любая добавленная вами реализация в лучшем случае будет хрупкой.

1 голос
/ 19 февраля 2012

Если я правильно понял, вы хотите манипулировать записями каталога, например, изменять имена файлов или добавлять фиктивные записи.Вы можете сделать это следующим образом.

Перегрузить opendir(), внутри него действительно открыть каталог с помощью "real" opendir(), сразу прочитать все записи каталога, с помощью "real" readdir(), изменить то, что необходимо,сохранить измененную версию в глобальной переменной и вернуть неизмененной DIR *.Затем в перегруженном readdir() вы воспринимаете переданный DIR * как свое собственное непрозрачное значение (например, введите карту) и просто последовательно возвращаете предварительно подготовленные записи.

Вот противное доказательство концепции (неприятно, потому что я пропустилскучная часть, такая как проверка ошибок, закрытие ресурса, освобождение памяти, безопасность потоков и т. д.):

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);
    }
}

Теперь скомпилируйте:

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

Запустите нормально:

$ ./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 = .

Запустите с оберткой:

$ 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 = .
...