Linux系统的chroot实现原理
前言
大学刚接触的linux的时候,折腾系统是家常便饭,在重装系统的时候经常使用chroot来配 置新的系统,当时觉得是很神奇,最近在看容器的时候,再次看到chroot,就决定对chroot 的源码是如何实现的,深入理解chroot在linux中的实现;chroot主要是改变当前进程以及 其子进程的root目录,因此可以为进程创建隔离的环境,更容易运行的debug程序;但是 chroot并不能创建安全的隔离环境,只是修改进程查找目录的方式。
实现
系统调用实现
Linux分为用户态和内核态,普通进程想要调用系统功能,只能通过系统调用来实现, chroot运行在内核,因此需要执行运行态的切换,调用系统函数ksyschroot函数.
// 通过filename指定新的root目录 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; // current表示当前的进程,调用set_fs_root来改变root目录 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; }
查找current进程
// current实际执行了get_current函数 #define current get_current() // get_current返回的是当前进程的task_struct结构 static __always_inline struct task_struct *get_current(void) { return this_cpu_read_stable(current_task); } // 在task_struct结构体中有个fs的fs_struct struct fs_struct *fs; // real fs struct struct fs_struct { int users; spinlock_t lock; seqcount_t seq; int umask; int in_exec; // root和pwd分别定义了根目录和当前目录 struct path root, pwd; } __randomize_layout; // path struct struct path { struct vfsmount *mnt; struct dentry *dentry; } __randomize_layout;
改变root目录
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; // 执行chroot操作,改变根目录 write_seqcount_end(&fs->seq); spin_unlock(&fs->lock); if (old_root.dentry) path_put(&old_root); }
总结
分析完chroot的原理之后,发现实现还是非常容易理解的,所谓的改变root目录,其实就是 改变进程的taskstruct中fs结构体中的root字段,实现不同的根目录查询。从实现角度看, chroot的确不能实现安全的环境隔离。