Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15531790
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2009-07-01 13:30:43

浅析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;    
}

阅读(3730) | 评论(0) | 转发(0) |
0

上一篇:场强

下一篇:频谱仪

给主人留下些什么吧!~~