От чего зависит порядок записи записей, возвращаемых getdents? - PullRequest
8 голосов
/ 15 января 2012

Справочная информация у меня есть существующее приложение, которое перечисляет записи каталога; strace показывает, что он просто вызывает getdents и перечисляет их в порядке возврата. Я хотел бы, чтобы они отображались в том же порядке, что и вызов ls без аргументов. Можно ли каким-то образом обновить данные каталога, чтобы добиться этого?

FS - это ext4, если это имеет значение.

Спасибо

Ответы [ 2 ]

8 голосов
/ 15 января 2012

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

#define _GNU_SOURCE 1
#include <stdio.h>
#include <dirent.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>

struct __dirstream
{
  int __fd;
  char *__data;
  size_t __allocation;
  size_t __offset;
  size_t __size;
  struct dirent __entry;
};

typedef struct _dirent_list {
  struct dirent *value;
  struct _dirent_list *next;
} dirent_list;

typedef struct _my_DIR {
  struct __dirstream orig;
  dirent_list *first_entry;
  int first_readdir;
} my_DIR;

DIR *opendir(const char *name) {
  DIR *(*orig_opendir)(const char*) = dlsym(RTLD_NEXT, "opendir");
  DIR *dir = orig_opendir(name);

  // save additional information along with the
  // original DIR structure
  my_DIR *my_dir = calloc(1, sizeof(*my_dir));
  my_dir->first_readdir = 1;
  memcpy(my_dir, dir, sizeof(*dir));
  return (DIR*)my_dir;
}

struct dirent *readdir(DIR *dir) {
  struct dirent *(*orig_readdir)(DIR*) = dlsym(RTLD_NEXT, "readdir");
  my_DIR *my_dir = (my_DIR*)dir;
  dirent_list *item;

  if (my_dir->first_readdir) {
    struct dirent *entry;
    while ((entry = orig_readdir(dir))) {
      // exercise for the reader:
      // implement insertion sort here 
      item = calloc(1, sizeof(*item));
      item->value = entry;
      item->next = my_dir->first_entry;
      my_dir->first_entry = item;
    }
    my_dir->first_readdir = 0;
  }

  if (!my_dir->first_entry)
    return NULL;

  item = my_dir->first_entry;
  struct dirent *result = item->value;
  my_dir->first_entry = item->next;
  free(item);

  return result;
}

Переопределяет opendir и readdir, чтобы возвращать записи в обратном порядке (вы также можете настроить это для сортировки). Вот как вы используете его с программой test, которая просто перечисляет записи каталога в порядке их получения:

$ gcc -Wall -shared -fPIC -o libhookdir.so hookdir.c -ldl
$ ./test
..
test
.
hookdir.c
libhookdir.so
test.c
$ LD_PRELOAD=./libhookdir.so ./test
test.c
libhookdir.so
hookdir.c
.
test
..

Хах! Это работает. Мы только что подключили функцию libc.

1 голос
/ 15 января 2012

Нет, вы не можете манипулировать метаданными файловой системы, чтобы getdents(2) возвращаемый каталог начинался в том же порядке, что и порядок сортировки, который ls(1) применяется к каталогу.

Вы всегда можетеИзмените вашу программу для сортировки записей, используя те же алгоритмы, которые предоставляет ls(1), хотя для сортировки каталога с N записями требуется как минимум O (N) памяти и O (N Log N) времени.Вам придется решить, стоит ли реализация, память и время для сортировки таким же образом, как ls(1).

...