linux目录实现,浅析linux中目录枚举的具体实现

本文详细介绍了Linux系统中目录枚举的实现原理和技术细节。通过分析opendir(), sys_getdents()及文件系统readdir()等核心函数的作用,揭示了如何在Linux中遍历目录并获取文件列表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浅析linux中目录枚举的具体实现

如果我们知道文件名,我们可以直接输入文件名来打开它,

但是如果一个目录下有什么文件我们不知道呢,那我们该怎么办呢,

那就需要使用listdir先列出当前目录下包含的所有文件和目录,

然后我们就可以从ls列出来的文件名中,输入需要访问的文件名进行文件访问了,

这其中起到关键作用的函数一共有3个,

(1).用户空间的opendir()库函数,

(2).内核空间系统调用sys_getdents()

(3).相应挂接上的文件系统的目录操作函数机的readdir()方法实现,

比如proc_dir_operations方法集中的proc_readdir()方法实现.

经过上面3个主要函数的通力协作,枚举目录下文件的工作就成了小case了[luther.gliethttp]

下面来看看具体源码实现.

1.库函数opendir

// 打开目录,获取目录方法集,比如proc_dir_operations

DIR* opendir( const char* dirpath )

{

DIR* dir = malloc(sizeof(DIR));

if (!dir)

goto Exit;

dir->_DIR_fd = open(dirpath, O_RDONLY|O_DIRECTORY); // 打开O_DIRECTORY,在sys_open==>__link_path_walk发现该标志LOOKUP_DIRECTORY后,

// 那么此次打开操作将只到最后一个'/'分隔符前面的内容,比如'/proc/sys/',那么将打开sys这个目录[luther.gliethttp]

// 同时赋给该目录操作方法集file->f_op = proc_dir_operations;

if (dir->_DIR_fd < 0)

{

free(dir);

dir = NULL;

}

else

{

dir->_DIR_avail = 0;

dir->_DIR_next = NULL;

pthread_mutex_init( &dir->_DIR_lock, NULL );

}

Exit:

return dir;

}

struct dirent*

readdir(DIR * dir)

{

struct dirent *entry = NULL;

pthread_mutex_lock( &dir->_DIR_lock );

entry = _readdir_unlocked(dir); // 使用sys_getdents系统调用读取目录[luther.gliethttp]

pthread_mutex_unlock( &dir->_DIR_lock );

return entry;

}

static int listdir(const char *name, int flags)

{

char tmp[4096];

DIR *d;

struct dirent *de;

d = opendir(name);

if(d == 0) {

fprintf(stderr, "opendir failed, %s\n", strerror(errno));

return -1;

}

while((de = readdir(d)) != 0){ // 循环读取,直到空,首先读取的是'.'和'..'目录[luther.gliethttp]

if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;

if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;

if ((flags & LIST_LONG) != 0) {

sprintf(tmp, "%s/%s", name, de->d_name);

listfile(tmp, flags);

} else {

printf("%s\n", de->d_name);

}

}

......

}

2.内核中系统调用函数sys_getdents()

sys_getdents

==>vfs_readdir(file, filldir, &buf);

==>file->f_op->readdir(file, buf, filler);

==>proc_dir_operations.proc_readdir返回目录

asmlinkage long sys_getdents(unsigned int fd, struct linux_dirent __user * dirent, unsigned int count)

{

struct file * file;

struct linux_dirent __user * lastdirent;

struct getdents_callback buf;

int error;

error = -EFAULT;

if (!access_ok(VERIFY_WRITE, dirent, count))

goto out;

error = -EBADF;

file = fget(fd);

if (!file)

goto out;

buf.current_dir = dirent; // 当前dir

buf.previous = NULL; // 前一个为空

buf.count = count; // 内存大小

buf.error = 0;

error = vfs_readdir(file, filldir, &buf);

if (error < 0)

goto out_putf;

error = buf.error;

lastdirent = buf.previous;

if (lastdirent) {

if (put_user(file->f_pos, &lastdirent->d_off))

error = -EFAULT;

else

error = count - buf.count;

}

out_putf:

fput(file);

out:

return error;

}

3.文件系统支持的readdir()函数,比如proc_readdir

int proc_readdir(struct file *filp, void *dirent, filldir_t filldir)

{

struct inode *inode = filp->f_path.dentry->d_inode;

return proc_readdir_de(PDE(inode), filp, dirent, filldir);

}

int proc_readdir_de(struct proc_dir_entry *de, struct file *filp, void *dirent,

filldir_t filldir)

{

unsigned int ino;

int i;

struct inode *inode = filp->f_path.dentry->d_inode; // /proc/sys/对应的inode

int ret = 0;

lock_kernel();

ino = inode->i_ino;

if (!de) {

ret = -EINVAL;

goto out;

}

i = filp->f_pos; // 这里f_pos表示到目前为止一共读取了多少个目录下的数据了.

switch (i) {

case 0: // 第一次读取

if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)

goto out;

i++;

filp->f_pos++;

/* fall through */

case 1: // 第二次读取

if (filldir(dirent, "..", 2, i,

parent_ino(filp->f_path.dentry),

DT_DIR) < 0)

goto out;

i++;

filp->f_pos++;

/* fall through */

default:

spin_lock(&proc_subdir_lock);

// 接下来就是读取当前/proc/sys/目录下存在的目录了,sys目录下的所有文件和目录都挂接在

// de->subdir中.

de = de->subdir;

i -= 2; // 忽略上面'.'和'..'所占的2个计数

for (;;) {

if (!de) {

ret = 1;

spin_unlock(&proc_subdir_lock);

goto out;

}

if (!i)

break;

de = de->next;

i--;

}

do {

struct proc_dir_entry *next;

/* filldir passes info to user space */

de_get(de);

spin_unlock(&proc_subdir_lock);

// 将一个个dirent顺序填入lib库指定的缓冲区,lib库指定的默认缓冲区一次只能容纳15个dirent:

// struct DIR

// {

// int _DIR_fd;

// size_t _DIR_avail;

// struct dirent* _DIR_next;

// pthread_mutex_t _DIR_lock;

// struct dirent _DIR_buff[15]; // 所以lib库一次最多读取15个dirent目录下的内容[luther.gliethttp]

// };

// 在filldir()函数中,

// dirent = buf->previous;

//    if (dirent) {

//    如果previous存在了,那么将filp->f_pos存入dirent->d_off,

//        if (__put_user(offset, &dirent->d_off))

//            goto efault;

//    }

if (filldir(dirent, de->name, de->namelen, filp->f_pos,

de->low_ino, de->mode >> 12) < 0) {

de_put(de);

goto out;

}

spin_lock(&proc_subdir_lock);

filp->f_pos++; // 读取目录下文件个数计数加1[luther.gliethttp]

next = de->next;

de_put(de);

de = next;

} while (de);

spin_unlock(&proc_subdir_lock);

}

ret = 1;

out:    unlock_kernel();

return ret;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值