博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ls命令是怎样实现的,getdents64,linux-2.6.27.5
阅读量:2433 次
发布时间:2019-05-10

本文共 12432 字,大约阅读时间需要 41 分钟。

http://www.cppblog.com/momoxiao/archive/2010/04/04/111594.html

先通过strace来看下ls命令的执行都做了哪些系统调用:

strace 
-
o ls.txt ls 

运行结果,这儿只摘取了ls.txt中我们感兴趣的部分:

open(
"
.
"
, O_RDONLY
|
O_NONBLOCK
|
O_LARGEFILE
|
O_DIRECTORY
|
O_CLOEXEC) 
=
 
3
 
///
打开当前目录这个文件(目录是一种特殊的文件),并返回文件句柄3
fstat64(
3
, {st_mode
=
S_IFDIR
|
0755
, st_size
=
4096
}) 
=
 
0
   
///
取得当前目录文件的属性,比如这里大小为4096
fcntl64(
3
, F_GETFD)   
=
 
0x1
 (flags FD_CLOEXEC)
getdents64(
3
/*
 33 entries 
*/
4096
=
 
1104
     
///
读取当前目录下的文件
getdents64(
3
/*
 0 entries 
*/
4096
)  
=
 
0
close(
3
)                        
=
 
0
        
///
关闭当前目录文件的句柄

这里核心是getdents64系统调用,它读取目录文件中的一个个目录项(directory entry)并返回,所以我们运行ls后才看到文件。

下面我们就看下getdents64是怎么用的,想办法干扰它的执行,从而隐藏掉我们不想让用户发现的文件。

fs/readdir.c

asmlinkage 
long
 sys_getdents64(unsigned 
int
 fd, 
struct
 linux_dirent64 __user 
*
 dirent, unsigned 
int
 count)
{
    
struct
 file 
*
 file;
    
struct
 linux_dirent64 __user 
*
 lastdirent;
    
struct
 getdents_callback64 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;
    buf.previous 
=
 NULL;
    buf.count 
=
 count;
    buf.error 
=
 
0
;
    error 
=
 vfs_readdir(file, filldir64, 
&
buf); 
///
读取目录函数
    
if
 (error 
<
 
0
)
        
goto
 out_putf;
    error 
=
 buf.error;
    lastdirent 
=
 buf.previous;
    
if
 (lastdirent) {
        
typeof
(lastdirent
->
d_off) d_off 
=
 file
->
f_pos;
        error 
=
 
-
EFAULT;
        
if
 (__put_user(d_off, 
&
lastdirent
->
d_off))
            
goto
 out_putf;
        error 
=
 count 
-
 buf.count;
    }
out_putf:
    fput(file);
out
:
    
return
 error;
}

首先,在sys_getdents64中通过调用vfs_readdir()读取目录函数。

那么什么是vfs呢?vfs全名Virtual File Switch,就是虚拟文件系统。我们可以把Linux的文件系统看成三层,最上层是上层用户使用的系统调用,中间一层就是vfs,最下面一层是挂载到VFS中的各种实际文件系统,比如ext2,jffs等。Switch这个词在这儿用的很形象,上层同一个系统调用,在vfs这层会根据文件系统的类型,调用对应的内核函数。vfs这层,本身就是起一个switch的作用。
看下vfs_readdir()吧。
fs/readdir.c

int
 vfs_readdir(
struct
 file 
*
file, filldir_t filler, 
void
 
*
buf)
{
    
struct
 inode 
*
inode 
=
 file
->
f_path.dentry
->
d_inode;
    
int
 res 
=
 
-
ENOTDIR;
    
if
 (
!
file
->
f_op 
||
 
!
file
->
f_op
->
readdir)
        
goto
 
out
;
    res 
=
 security_file_permission(file, MAY_READ);
    
if
 (res)
        
goto
 
out
;
    res 
=
 mutex_lock_killable(
&
inode
->
i_mutex);
    
if
 (res)
        
goto
 
out
;
    res 
=
 
-
ENOENT;
    
if
 (
!
IS_DEADDIR(inode)) {
        res 
=
 file
->
f_op
->
readdir(file, buf, filler); 
///
调用实际文件系统的读取目录项(就是文件系统三层结构中最下面一层)
        file_accessed(file);
    }
    mutex_unlock(
&
inode
->
i_mutex);
out
:
    
return
 res;
}

里面file->f_op->readdir()读取底层实际文件系统的目录项。

大致的关系是这样的:
file结构里有个文件操作的函数集const struct file_operations *f_op。
struct file_operations 中实际上是一些函数的指针,readdir就是其中的一个指针。
在调用vir_readdir之前,内核会根据实际文件系统类型给struct file_operations赋对应值。
下面我们通过看代码,获得一个比较直观的认识。
struct file 和 struct file_operations都在/include/linux/fs.h中定义。
file结构:

struct
 file {
    
/*
     * fu_list becomes invalid after file_free is called and queued via
     * fu_rcuhead for RCU freeing
     
*/
    union {
        
struct
 list_head    fu_list;
        
struct
 rcu_head     fu_rcuhead;
    } f_u;
    
struct
 path        f_path;
#define
 f_dentry    f_path.dentry
#define
 f_vfsmnt    f_path.mnt
    
const
 
struct
 file_operations    
*
f_op; 
///
对应每一种实际的文件系统,会有自己的file_operations函数集。可以理解成file这个类的纯虚函数集
    atomic_long_t        f_count;
    unsigned 
int
         f_flags;
    mode_t            f_mode;
    loff_t            f_pos;
    
struct
 fown_struct    f_owner;
    unsigned 
int
        f_uid, f_gid;
    
struct
 file_ra_state    f_ra;
    u64            f_version;
#ifdef CONFIG_SECURITY
    
void
            
*
f_security;
#endif
    
/*
 needed for tty driver, and maybe others 
*/
    
void
            
*
private_data;
#ifdef CONFIG_EPOLL
    
/*
 Used by fs/eventpoll.c to link all the hooks to this file 
*/
    
struct
 list_head    f_ep_links;
    spinlock_t        f_ep_lock;
#endif
 /* #ifdef CONFIG_EPOLL */
    
struct
 address_space    
*
f_mapping;
#ifdef CONFIG_DEBUG_WRITECOUNT
    unsigned 
long
 f_mnt_write_state;
#endif
};
file_operations结构,里面是一些函数指针。我们在这儿关心的是int (*readdir) (struct file *, void *, filldir_t);
readdir()用来读取实际文件系统目录项。
struct
 file_operations {
    
struct
 module 
*
owner;
    loff_t (
*
llseek) (
struct
 file 
*
, loff_t, 
int
);
    ssize_t (
*
read) (
struct
 file 
*
char
 __user 
*
, size_t, loff_t 
*
);
    ssize_t (
*
write) (
struct
 file 
*
const
 
char
 __user 
*
, size_t, loff_t 
*
);
    ssize_t (
*
aio_read) (
struct
 kiocb 
*
const
 
struct
 iovec 
*
, unsigned 
long
, loff_t);
    ssize_t (
*
aio_write) (
struct
 kiocb 
*
const
 
struct
 iovec 
*
, unsigned 
long
, loff_t);
    
int
 (
*
readdir) (
struct
 file 
*
void
 
*
, filldir_t);  
///
我们在这儿关心的函数指针,实际文件系统的读取目录项函数。
                
///
每次打开文件,内核都会根据文件位于的文件系统类型,对文件相应的file_operations赋相应值。
    unsigned 
int
 (
*
poll) (
struct
 file 
*
struct
 poll_table_struct 
*
);
    
int
 (
*
ioctl) (
struct
 inode 
*
struct
 file 
*
, unsigned 
int
, unsigned 
long
);
    
long
 (
*
unlocked_ioctl) (
struct
 file 
*
, unsigned 
int
, unsigned 
long
);
    
long
 (
*
compat_ioctl) (
struct
 file 
*
, unsigned 
int
, unsigned 
long
);
    
int
 (
*
mmap) (
struct
 file 
*
struct
 vm_area_struct 
*
);
    
int
 (
*
open) (
struct
 inode 
*
struct
 file 
*
);
    
int
 (
*
flush) (
struct
 file 
*
, fl_owner_t id);
    
int
 (
*
release) (
struct
 inode 
*
struct
 file 
*
);
    
int
 (
*
fsync) (
struct
 file 
*
struct
 dentry 
*
int
 datasync);
    
int
 (
*
aio_fsync) (
struct
 kiocb 
*
int
 datasync);
    
int
 (
*
fasync) (
int
struct
 file 
*
int
);
    
int
 (
*
lock
) (
struct
 file 
*
int
struct
 file_lock 
*
);
    ssize_t (
*
sendpage) (
struct
 file 
*
struct
 page 
*
int
, size_t, loff_t 
*
int
);
    unsigned 
long
 (
*
get_unmapped_area)(
struct
 file 
*
, unsigned 
long
, unsigned 
long
, unsigned 
long
, unsigned 
long
);
    
int
 (
*
check_flags)(
int
);
    
int
 (
*
dir_notify)(
struct
 file 
*
filp, unsigned 
long
 arg);
    
int
 (
*
flock) (
struct
 file 
*
int
struct
 file_lock 
*
);
    ssize_t (
*
splice_write)(
struct
 pipe_inode_info 
*
struct
 file 
*
, loff_t 
*
, size_t, unsigned 
int
);
    ssize_t (
*
splice_read)(
struct
 file 
*
, loff_t 
*
struct
 pipe_inode_info 
*
, size_t, unsigned 
int
);
    
int
 (
*
setlease)(
struct
 file 
*
long
struct
 file_lock 
**
);
};
下面来看下在ls用到file结构中的file_operations之前,内核是怎样它赋值的
struct
 inode 
*
ext2_iget (
struct
 super_block 
*
sb, unsigned 
long
 ino)
{
    
struct
 ext2_inode_info 
*
ei;
    
struct
 buffer_head 
*
 bh;
    
struct
 ext2_inode 
*
raw_inode;
    
struct
 inode 
*
inode;
    
long
 ret 
=
 
-
EIO;
    
int
 n;
    inode 
=
 iget_locked(sb, ino);
    
if
 (
!
inode)
        
return
 ERR_PTR(
-
ENOMEM);
    
if
 (
!
(inode
->
i_state 
&
 I_NEW))
        
return
 inode;
    ei 
=
 EXT2_I(inode);
#ifdef CONFIG_EXT2_FS_POSIX_ACL
    ei
->
i_acl 
=
 EXT2_ACL_NOT_CACHED;
    ei
->
i_default_acl 
=
 EXT2_ACL_NOT_CACHED;
#endif
    ei
->
i_block_alloc_info 
=
 NULL;
    raw_inode 
=
 ext2_get_inode(inode
->
i_sb, ino, 
&
bh);
    
if
 (IS_ERR(raw_inode)) {
        ret 
=
 PTR_ERR(raw_inode);
         
goto
 bad_inode;
    }
    inode
->
i_mode 
=
 le16_to_cpu(raw_inode
->
i_mode);
    inode
->
i_uid 
=
 (uid_t)le16_to_cpu(raw_inode
->
i_uid_low);
    inode
->
i_gid 
=
 (gid_t)le16_to_cpu(raw_inode
->
i_gid_low);
    
if
 (
!
(test_opt (inode
->
i_sb, NO_UID32))) {
        inode
->
i_uid 
|=
 le16_to_cpu(raw_inode
->
i_uid_high) 
<<
 
16
;
        inode
->
i_gid 
|=
 le16_to_cpu(raw_inode
->
i_gid_high) 
<<
 
16
;
    }
    inode
->
i_nlink 
=
 le16_to_cpu(raw_inode
->
i_links_count);
    inode
->
i_size 
=
 le32_to_cpu(raw_inode
->
i_size);
    inode
->
i_atime.tv_sec 
=
 (signed)le32_to_cpu(raw_inode
->
i_atime);
    inode
->
i_ctime.tv_sec 
=
 (signed)le32_to_cpu(raw_inode
->
i_ctime);
    inode
->
i_mtime.tv_sec 
=
 (signed)le32_to_cpu(raw_inode
->
i_mtime);
    inode
->
i_atime.tv_nsec 
=
 inode
->
i_mtime.tv_nsec 
=
 inode
->
i_ctime.tv_nsec 
=
 
0
;
    ei
->
i_dtime 
=
 le32_to_cpu(raw_inode
->
i_dtime);
    
/*
 We now have enough fields to check if the inode was active or not.
     * This is needed because nfsd might try to access dead inodes
     * the test is that same one that e2fsck uses
     * NeilBrown 1999oct15
     
*/
    
if
 (inode
->
i_nlink 
==
 
0
 
&&
 (inode
->
i_mode 
==
 
0
 
||
 ei
->
i_dtime)) {
        
/*
 this inode is deleted 
*/
        brelse (bh);
        ret 
=
 
-
ESTALE;
        
goto
 bad_inode;
    }
    inode
->
i_blocks 
=
 le32_to_cpu(raw_inode
->
i_blocks);
    ei
->
i_flags 
=
 le32_to_cpu(raw_inode
->
i_flags);
    ei
->
i_faddr 
=
 le32_to_cpu(raw_inode
->
i_faddr);
    ei
->
i_frag_no 
=
 raw_inode
->
i_frag;
    ei
->
i_frag_size 
=
 raw_inode
->
i_fsize;
    ei
->
i_file_acl 
=
 le32_to_cpu(raw_inode
->
i_file_acl);
    ei
->
i_dir_acl 
=
 
0
;
    
if
 (S_ISREG(inode
->
i_mode))
        inode
->
i_size 
|=
 ((__u64)le32_to_cpu(raw_inode
->
i_size_high)) 
<<
 
32
;
    
else
        ei
->
i_dir_acl 
=
 le32_to_cpu(raw_inode
->
i_dir_acl);
    ei
->
i_dtime 
=
 
0
;
    inode
->
i_generation 
=
 le32_to_cpu(raw_inode
->
i_generation);
    ei
->
i_state 
=
 
0
;
    ei
->
i_block_group 
=
 (ino 
-
 
1
/
 EXT2_INODES_PER_GROUP(inode
->
i_sb);
    ei
->
i_dir_start_lookup 
=
 
0
;
    
/*
     * NOTE! The in-memory inode i_data array is in little-endian order
     * even on big-endian machines: we do NOT byteswap the block numbers!
     
*/
    
for
 (n 
=
 
0
; n 
<
 EXT2_N_BLOCKS; n
++
)
        ei
->
i_data[n] 
=
 raw_inode
->
i_block[n];
///
下面是我们关心的。。。。。。。。。。。。。。。。。。。。。。。。
///
这里对inode->fop赋值,就是inode中的file_operations结构。
    
if
 (S_ISREG(inode
->
i_mode)) {   
///
普通文件(S_ISREG),inode->i_fop为ext2_file_operations函数集
        inode
->
i_op 
=
 
&
ext2_file_inode_operations;
        
if
 (ext2_use_xip(inode
->
i_sb)) {   
///
???现在不关心
            inode
->
i_mapping
->
a_ops 
=
 
&
ext2_aops_xip;
            inode
->
i_fop 
=
 
&
ext2_xip_file_operations;
        } 
else
 
if
 (test_opt(inode
->
i_sb, NOBH)) {
            inode
->
i_mapping
->
a_ops 
=
 
&
ext2_nobh_aops;
            inode
->
i_fop 
=
 
&
ext2_file_operations;
        } 
else
 {
            inode
->
i_mapping
->
a_ops 
=
 
&
ext2_aops;
            inode
->
i_fop 
=
 
&
ext2_file_operations;
        }
    } 
else
 
if
 (S_ISDIR(inode
->
i_mode)) {   
///
目录文件(S_ISDIR),inode->i_fop为ext2_dir_operations函数集
        inode
->
i_op 
=
 
&
ext2_dir_inode_operations;
        inode
->
i_fop 
=
 
&
ext2_dir_operations;
        
if
 (test_opt(inode
->
i_sb, NOBH))
            inode
->
i_mapping
->
a_ops 
=
 
&
ext2_nobh_aops;
        
else
            inode
->
i_mapping
->
a_ops 
=
 
&
ext2_aops;
    } 
else
 
if
 (S_ISLNK(inode
->
i_mode)) {   
///
链接文件(S_ISLNK),不需要inode->i_fop函数集
        
if
 (ext2_inode_is_fast_symlink(inode))
            inode
->
i_op 
=
 
&
ext2_fast_symlink_inode_operations;
        
else
 {
            inode
->
i_op 
=
 
&
ext2_symlink_inode_operations;
            
if
 (test_opt(inode
->
i_sb, NOBH))
                inode
->
i_mapping
->
a_ops 
=
 
&
ext2_nobh_aops;
            
else
                inode
->
i_mapping
->
a_ops 
=
 
&
ext2_aops;
        }
    } 
else
 {
        inode
->
i_op 
=
 
&
ext2_special_inode_operations;
        
if
 (raw_inode
->
i_block[
0
])
            init_special_inode(inode, inode
->
i_mode,
               old_decode_dev(le32_to_cpu(raw_inode
->
i_block[
0
])));
        
else
            init_special_inode(inode, inode
->
i_mode,
               new_decode_dev(le32_to_cpu(raw_inode
->
i_block[
1
])));
    }
    
///
以上。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    brelse (bh);
    ext2_set_inode_flags(inode);
    unlock_new_inode(inode);
    
return
 inode;
bad_inode:
    iget_failed(inode);
    
return
 ERR_PTR(ret);
}
上面一段代码把inode中的file_operations赋值为ext2_file_operations。
打开文件用sys_open(),在fs/open.c文件中,函数调用流程如下:
sys_open() --> do_sys_open() --> do_filp_open() --> nameidata_to_filp() --> __dentry_open()
static
 
struct
 file 
*
__dentry_open(
struct
 dentry 
*
dentry, 
struct
 vfsmount 
*
mnt,
                    
int
 flags, 
struct
 file 
*
f,
                    
int
 (
*
open)(
struct
 inode 
*
struct
 file 
*
))
{
    
struct
 inode 
*
inode;
    
int
 error;
    f
->
f_flags 
=
 flags;
    f
->
f_mode 
=
 ((flags
+
1
&
 O_ACCMODE) 
|
 FMODE_LSEEK 
|
                FMODE_PREAD 
|
 FMODE_PWRITE;
    inode 
=
 dentry
->
d_inode;
    
if
 (f
->
f_mode 
&
 FMODE_WRITE) {
        error 
=
 __get_file_write_access(inode, mnt);
        
if
 (error)
            
goto
 cleanup_file;
        
if
 (
!
special_file(inode
->
i_mode))
            file_take_write(f);
    }
    f
->
f_mapping 
=
 inode
->
i_mapping;
    f
->
f_path.dentry 
=
 dentry;
    f
->
f_path.mnt 
=
 mnt;
    f
->
f_pos 
=
 
0
;
    f
->
f_op 
=
 fops_get(inode
->
i_fop);   
///
把inode中file_operations函数集给file中file_operations函数集
    file_move(f, 
&
inode
->
i_sb
->
s_files);
    error 
=
 security_dentry_open(f);
    
if
 (error)
        
goto
 cleanup_all;
    
if
 (
!
open 
&&
 f
->
f_op)
        open 
=
 f
->
f_op
->
open;
    
if
 (open) {
        error 
=
 open(inode, f);
        
if
 (error)
            
goto
 cleanup_all;
    }
    f
->
f_flags 
&=
 
~
(O_CREAT 
|
 O_EXCL 
|
 O_NOCTTY 
|
 O_TRUNC);
    file_ra_state_init(
&
f
->
f_ra, f
->
f_mapping
->
host
->
i_mapping);
    
/*
 NB: we're sure to have correct a_ops only after f_op->open 
*/
    
if
 (f
->
f_flags 
&
 O_DIRECT) {
        
if
 (
!
f
->
f_mapping
->
a_ops 
||
            ((
!
f
->
f_mapping
->
a_ops
->
direct_IO) 
&&
            (
!
f
->
f_mapping
->
a_ops
->
get_xip_mem))) {
            fput(f);
            f 
=
 ERR_PTR(
-
EINVAL);
        }
    }
    
return
 f;
cleanup_all:
    fops_put(f
->
f_op);
    
if
 (f
->
f_mode 
&
 FMODE_WRITE) {
        put_write_access(inode);
        
if
 (
!
special_file(inode
->
i_mode)) {
            
/*
             * We don't consider this a real
             * mnt_want/drop_write() pair
             * because it all happenend right
             * here, so just reset the state.
             
*/
            file_reset_write(f);
            mnt_drop_write(mnt);
        }
    }
    file_kill(f);
    f
->
f_path.dentry 
=
 NULL;
    f
->
f_path.mnt 
=
 NULL;
cleanup_file:
    put_filp(f);
    dput(dentry);
    mntput(mnt);
    
return
 ERR_PTR(error);
}
在这儿,f
->
f_op 
=
 fops_get(inode
->
i_fop); 把file结构中的file_operations函数集赋值成inode中的函数集,也就是ext2_file_operations。
下面归纳下ls执行的整个流程:
假设当前目录在ext2文件系统上,ls要查看当前目录下的文件,
1.open打开当前目录的句柄,这个句柄对应内核中一个file结构。
   file结构中的file_operations函数集从inode结构中获得,就是ext2_file_operations
2.getdents64调用file->f_op->readdir()实际上是调用了ext2_file_operations中的readdir(),
   由ext2文件系统驱动读取当前目录下面的文件项。
我们要隐藏一个文件,要做的就是替换file->f_op->readdir(),也就是替换ext2_file_operations中的readdir()。

转载地址:http://orlmb.baihongyu.com/

你可能感兴趣的文章
【Java】【数据库】知识重点——数据库篇
查看>>
【Java】学习总结 —— HashMap之put()方法实现原理
查看>>
【计算机网络】【TCP】如何讲清楚Tcp的三次握手和四次挥手?
查看>>
【Java】-- Java核心知识点总结
查看>>
【数据库】SQL之重点知识点总结
查看>>
【Java】【Web】JavaWeb相关知识总结 2018-9-17
查看>>
【数据库】突破单一数据库的性能限制——数据库-分库分表总结 2018-9-20
查看>>
Slurm——作业调度处理
查看>>
Lustre 维护
查看>>
Lustre—磁盘配额测试
查看>>
SSH加密密码中的非对称式密码学
查看>>
Mac Redis安装入门教程
查看>>
python3安装教程配置配置阿里云
查看>>
Mac快捷键和实用技巧
查看>>
Git的多人协作和分支处理测试
查看>>
mysql索引回表
查看>>
iterm2 保存阿里云登陆并防止断开连接
查看>>
brew安装
查看>>
mysql5.7初始密码查看及密码重置
查看>>
go语言实现2048小游戏(完整代码)
查看>>