Linux系统的chroot实现原理

Table of Contents

前言

大学刚接触的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的确不能实现安全的环境隔离。