先通过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, http://www.cppblog.com/Images/dot.gif}) = 0 ///取得当前目录文件的属性,比如这里大小为4096
fcntl64(3, F_GETFD) = 0x1 (flags FD_CLOEXEC)
getdents64(3, , 4096) = 1104 ///读取当前目录下的文件
getdents64(3, , 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 {
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
void *private_data;
#ifdef CONFIG_EPOLL
struct list_head f_ep_links;
spinlock_t