C 访问inode表以列出所有文件名

C 访问inode表以列出所有文件名,c,filesystems,inode,cifs,C,Filesystems,Inode,Cifs,我想知道在Posix系统上列出文件名的最有效方法。执行以下任一操作: $ ls -R 或: 或: 或100种其他变体(StackOverflow/ServerFault上有大量关于各种方法的链接)。然而,这在我所在的文件系统上太慢了,例如,我目前运行ls-R大约两天(完成大约需要50个小时——系统上有大量的文件和目录——价值数PB) 所以我想知道这是否可以在较低的级别上完成,最好是在C中。从inode数据库中列出文件名(示例)。我不需要对整个路径进行递归查找,只需要顶级的name | file

我想知道在Posix系统上列出文件名的最有效方法。执行以下任一操作:

$ ls -R
或:

或:

或100种其他变体(StackOverflow/ServerFault上有大量关于各种方法的链接)。然而,这在我所在的文件系统上太慢了,例如,我目前运行
ls-R
大约两天(完成大约需要50个小时——系统上有大量的文件和目录——价值数PB)

所以我想知道这是否可以在较低的级别上完成,最好是在C中。从
inode
数据库中列出文件名(示例)。我不需要对整个路径进行递归查找,只需要顶级的
name | filename
——我可以手动构建其他所有内容。是否有一种方法可以做到这一点,这样就可以转储inode数据库本身,而不是用大约50个小时来执行一个包含数十亿次递归查找的
ls
命令(是的,它在连续运行后会被缓存,但在第一次运行时不会缓存大部分)

举个例子,比如:

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

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

这里有一种直接递归使用的方法。我将很快更新它的计时,以将其与
ls
和其他标准unix UTIL进行比较:

#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);
}
定义GNU源
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义句柄错误(msg)do{perror(msg);退出(退出失败);}while(0)
结构linux\u目录{
未签名的长d_ino;
关(off)t d(off);;
无符号短d_recen;
字符d_名称[];
};
作废打印文件(字符*目录,文件*输出)
{
//打开文件
int fd=open(dir,O|RDONLY | O|u目录);
如果(fd==-1)处理_错误(“打开文件时出错”。\n”);
//抓取一个缓冲区来读取文件数据
#定义基本单位大小(1024*1024*1)
char*buffer=malloc(sizeof*buffer*BUF_SIZE);
if(buffer==NULL)处理_错误(“error malloc.\n”);
//执行getdents系统调用写入缓冲区
int num_read=syscall(SYS_getdents、fd、buffer、BUF_SIZE);
如果(num_read==-1)处理_错误(“error getdents syscall.\n”);
关闭(fd);
用于(长缓冲区位置=0;缓冲区位置d_重新设置-1);
//跳过列表中的.和
如果(d->d_名称[0]='.')){
缓冲器位置+=d->d\U重新开启;
继续;
}
//path=dir+'/'+名称
字符路径[400];
strcpy(路径,方向);
strcat(路径“/”;
strcat(路径,d->d_名称);
//根据需要进行递归调用
if(d_type==DT_DIR)
打印_文件(路径,输出);
else if(d_type==DT_REG)
fprintf(输出,“%s\n”,路径);
//提前缓冲位置
缓冲器位置+=d->d\U重新开启;
}
自由(缓冲);
}
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”);
打印_文件(dir,out);
}

ls-i
也会转储inode编号,但我猜这不是您想要的。您是否也可以分享一个示例,说明您希望从示例目录树的输出中得到什么?我无法理解
不需要递归查找整个路径,只需要顶级的
位。@Zoso我刚刚添加了一个更新。我指的是文件名而不是整个路径,所以,
myfile.mov
而不是
/mnt/something/something/somethine/path/to/myfile.mov
@Zoso关于
ls-I
——这很好,我只关心速度,所以不管需要做什么。inode不包含文件名。文件名仅存在于目录项中。这就是允许多个硬链接指向同一个物理文件的原因。@David542我怀疑它是否会直接使用系统调用,而是使用glibc包装,然后再使用
getdents
。看起来不错。在
main()
中为
fopen
添加一些错误检查,不确定字符路径[400]
的长度,可能会将其放大?。
#filename,inode
myfile.mov,1234
myotherfile.csv,92033
#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);
}