chroot 时做了什么
2019-12-31 23:18 linux
debian 社区,大都使用 chroot 环境做一些干净的编译打包操作,之前手动安装 debian 和 archlinux 系统时,也有在使用 chroot。但并不了解 chroot 时,系统究竟发生了什么。 在 2019 年最后一天,找时间看了下内核代码,才了解到里面的操作。
先说结论:chroot 改变当前进程所在的内核空间的一个叫做 current->root
的全局变量,
之后该进程所有的文件系统操作的路径,都以新的 path
作为根目录。
首先看 fs/open.c
中实现的 ksys_chroot()
系统调用:
int ksys_chroot(const char __user *filename)
{
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
retry:
error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
if (error)
goto out;
error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
error = -EPERM;
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;
}
它最终调用了 fs/fs_struct.c
中的 set_fs_root()
函数:
/*
* 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;
write_seqcount_end(&fs->seq);
spin_unlock(&fs->lock);
if (old_root.dentry)
path_put(&old_root);
}
而这里用到的 current
全局变量, 在 x86
架构中的定义位于 arch/x86/include/asm/current.h
:
struct task_struct;
DECLARE_PER_CPU(struct task_struct *, current_task);
static __always_inline struct task_struct *get_current(void)
{
return this_cpu_read_stable(current_task);
}
#define current get_current()
总之,除了文件系统之外,chroot
并没有提供额外的资源隔离。
另外,默认情况下,在 chroot
环境里,进程也无法访问文件系统及进程信息,因为它们
分别位于 /dev
及 /proc
,只有手动挂载它们到 chroot
所在的目录才行。