mount 内核源码_Linux 内核源码分析 -- chroot

本文分析了Linux 5.6版本中chroot系统调用的实现过程,从user_path_at函数查找路径到inode_permission权限检查,再到security_path_chroot的安全校验,重点介绍了set_fs_root如何改变进程的根目录。通过对fs_struct、path、vfsmount和dentry结构的理解,揭示了chroot如何修改进程的根目录视图。
摘要由CSDN通过智能技术生成

chroot 在 内核中的实现

在 Linux 5.6 版本中 chroot 函数的系统调用对应的函数位于:./fs/open.c:539:SYSCALL_DEFINE1(chroot, const char __user *, filename)

SYSCALL_DEFINE1(chroot, const char __user *, filename)

{

return ksys_chroot(filename);

}

ksys_chroot

int ksys_chroot(const char __user *filename)

{

struct path path;

int error;

unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;

retry:

// 根据文件名找到 path 结构

error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);

if (error)

goto out;

// 解析 path 的 mm_root dentry 结构,再解析相应的 inode 结构,即 d_inode,就可找到挂载点相应的 inode 结构

error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);

if (error)

goto dput_and_out;

error = -EPERM;

// 判断当前进程所有者是不是有执行 chroot 操作的权限

// 这里是 namespace, cred 的内容了,不展开

if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))

goto dput_and_out;

error = security_path_chroot(&path);

if (error)

goto dput_and_out;

// 主要操作就是这个函数

set_fs_root(current->fs, &path);

error = 0;

dput_and_out:

path_put(&path);

if (retry_estale(error, lookup_flags)) {

lookup_flags |= LOOKUP_REVAL;

goto retry;

}

out:

return error;

}

flag 含义

#define LOOKUP_FOLLOW0x0001/* follow links at the end */

#define LOOKUP_DIRECTORY0x0002/* require a directory */

set_fs_root

主要函数,就是在这个函数里修改了程序的 “根目录”

先来看一下 fs_struct

struct fs_struct {

int users;

spinlock_t lock;

seqcount_t seq;

int umask;

int in_exec;

struct path root, pwd;

// root:根目录的目录项

// pwd:当前工作目录的目录项

} __randomize_layout;

/*

* Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.

* It can block.

*/

void set_fs_root(struct fs_struct *fs, const struct path *path)

{

struct path old_root;

path_get(path);

spin_lock(&fs->lock); // 自旋锁

write_seqcount_begin(&fs->seq);

old_root = fs->root; // 保存程序的 根目录 的目录项

fs->root = *path; // 设置 根目录 为 path 的目录项

write_seqcount_end(&fs->seq);

spin_unlock(&fs->lock);

if (old_root.dentry)

path_put(&old_root);

}

struct path

struct path {

struct vfsmount *mnt;

struct dentry *dentry;

} __randomize_layout;

struct vfsmount

描述独立文件系统的挂载信息,每个不同的挂载点对应一个独立的 vfsmount 结构,属于同一文件系统的所有目录和文件隶属同一 vfsmount 该 vfsmount 结构对应于该文件系统顶层目录,即挂载目录

struct vfsmount {

struct dentry *mnt_root;/* 上一层挂载点对应的 dentry */

struct super_block *mnt_sb;/* 指向超级块 */

int mnt_flags;

} __randomize_layout;

struct dentry

目录项,是Linux文件系统中某个 索引节点(inode) 的链接

struct dentry {

/* RCU lookup touched fields */

unsigned int d_flags;/* protected by d_lock */

seqcount_t d_seq;/* per dentry seqlock */

struct hlist_bl_node d_hash;/* lookup hash list */

struct dentry *d_parent;/* 父目录项指针 */

struct qstr d_name; // 文件或者目录的名称

// 目录的 inode

struct inode *d_inode;/* Where the name belongs to - NULL is

* negative */

unsigned char d_iname[DNAME_INLINE_LEN];/* small names */

/* Ref lookup also touches following */

struct lockref d_lockref;/* per-dentry lock and refcount */

const struct dentry_operations *d_op;

struct super_block *d_sb;/* The root of the dentry tree */

unsigned long d_time;/* used by d_revalidate */

void *d_fsdata;/* fs-specific data */

union {

struct list_head d_lru;/* LRU list */

wait_queue_head_t *d_wait;/* in-lookup ones only */

};

struct list_head d_child;/* child of parent list */

struct list_head d_subdirs;/* our children */

/*

* d_alias and d_rcu can share memory

*/

union {

struct hlist_node d_alias;/* inode alias list */

struct hlist_bl_node d_in_lookup_hash;/* only for in-lookup ones */

struct rcu_head d_rcu;

} d_u;

} __randomize_layout;

上面这两个都是 文件系统 的东西,不在这里详细分析

总结

其实 chroot 修改了进程的 root 目录的核心操作就是修改了 进程 的 task_struct -> fs -> root

因为是 struct path root,所以

user_path_at(AT_FDCWD, filename, lookup_flags, &path);

就是通过文件名去解析 文件夹 对应的 path 结构,存在 path 变量里面

然后就是权限检查

在然后把 path 传进 set_fs_root 函数

fs->root = *path;

修改了 root

这样进程就认为 filename 是根目录,因为 fs->root 存的是 filename 目录的 path 结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值