【问题标题】:Access inode table to list all filenames访问 inode 表以列出所有文件名
【发布时间】:2021-07-16 01:31:00
【问题描述】:

我想知道在 Posix 系统上列出文件名的最有效方法。做任何一个:

$ ls -R

或者:

$ find /

或者:

$ du /

或 100 种其他变体(StackOverflow/ServerFault 上有很多关于执行此操作的方法的链接)。但是,这在我所在的文件系统cifs 上太慢了——例如,我目前已经运行ls -R 大约两天(大约需要 50 小时才能完成——有大量文件和系统上的目录——价值数 PB)。

所以我想知道这是否可以在较低级别完成,希望在 C. 中列出 inode 数据库中的文件名(例如 here)。我不需要递归查找整个路径,只需要顶级name | filename——我会手动构建其他所有内容。有没有办法做到这一点,希望而不是花费大约 50 小时来执行具有数十亿次递归查找的ls 命令(是的,它确实在连续运行后被缓存,但在第一次运行时不会缓存大部分)可以转储inode数据库本身吗?

一个例子,也许是这样的:

#filename,inode
myfile.mov,1234
myotherfile.csv,92033

但这里的重点——以及我问这个问题的原因——是速度实际上并不是执行上述操作的命令(例如$ ls -iR)。

【问题讨论】:

  • ls -i 也会转储 inode 编号,但我猜这不是您想要的。您还可以分享一个示例,说明您对示例目录树的输出的期望吗?我无法理解don't need a recursive lookup of the entire path, but only the top-level 位。
  • @Zoso 我刚刚添加了一个更新。我的意思是文件名而不是整个路径,所以,myfile.mov 而不是 /mnt/something/somewhere/path/to/myfile.mov
  • @Zoso 关于ls -i——没关系,我只关心速度,所以无论做什么都需要这样做。
  • 索引节点不包含文件名。文件名存在于目录条目中。这就是允许多个硬链接指向同一个物理文件的原因。
  • @David542 我怀疑它会直接使用系统调用,而是使用readdir,这又是getdents 上的glibc 包装器。

标签: c filesystems inode cifs


【解决方案1】:

这是一种直接递归使用getdents的方法。我将很快更新它的时间以将其与ls 和其他标准 unix utils 进行比较:

#define _GNU_SOURCE
#include <dirent.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

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

void print_files(char* dir, FILE* out)
{
    // open the file
    int fd = open(dir, O_RDONLY | O_DIRECTORY);
    if (fd == -1) handle_error("Error opening file.\n");

    // grab a buffer to read the file data
    #define BUF_SIZE (1024*1024*1)
    char* buffer = malloc(sizeof *buffer * BUF_SIZE);
    if (buffer == NULL) handle_error("Error malloc.\n");

    // do the getdents syscall writing to buffer
    int num_read = syscall(SYS_getdents, fd, buffer, BUF_SIZE);
    if (num_read == -1) handle_error("Error getdents syscall.\n");
    close(fd);

    for (long buffer_position = 0; buffer_position < num_read;) {

        struct linux_dirent *d = (struct linux_dirent *) (buffer + buffer_position);
        char d_type = *(buffer + buffer_position + d->d_reclen - 1);

        // skip on . and .. in the listing
        if (d->d_name[0] == '.') {
            buffer_position += d->d_reclen;
            continue;
        }

        // path = dir + '/' + name
        char path[400];
        strcpy(path, dir);
        strcat(path, "/");
        strcat(path, d->d_name);

        // recursive call, as necessary
        if (d_type == DT_DIR)
             print_files(path, out);
        else if (d_type == DT_REG)
            fprintf(out, "%s\n", path);

        // advance buffer position
        buffer_position += d->d_reclen;

    }

    free(buffer);

}

int main(int argc, char *argv[])
{
    char dir[1024];
    strcpy(dir, argc > 1 ? argv[1] : ".");
    FILE *out = fopen("c-log.txt", "w");
    fprintf(out, "-------------[ START ]---------------------\n");
    print_files(dir, out);
}

【讨论】:

  • 看起来不错。在main() 中为fopen 添加一些错误检查,不确定char path[400] 的长度,可能会增加?
猜你喜欢
  • 2013-08-30
  • 2016-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-19
  • 2023-03-04
相关资源
最近更新 更多