- include/container.h: document struct fields and constants - src/main.c: explain argument parsing, clone flow, parent/child roles - src/mount.c: describe 6-step mount isolation process - src/cgroup.c: explain cgroup data structures and resource setup/cleanup - src/capabilities.c: document capability drop (bounding + inheritable) - src/seccomp.c: explain each seccomp rule's security purpose - src/userns.c: describe parent-child sync and uid_map/gid_map format - src/hostname.c: document tarot card random naming scheme
113 lines
3.6 KiB
C
113 lines
3.6 KiB
C
/* mount.c — 文件系统隔离模块
|
||
*
|
||
* 负责容器的文件系统隔离:
|
||
* 1. MS_PRIVATE 重挂载,防止挂载事件泄漏到宿主机
|
||
* 2. bind mount 将 rootfs 挂载到临时目录
|
||
* 3. pivot_root 切换根文件系统
|
||
* 4. 卸载旧的根文件系统
|
||
* 5. 挂载 /proc(进程信息)和 /dev(设备节点)
|
||
*/
|
||
|
||
#include "container.h"
|
||
#include "mount.h"
|
||
|
||
/* pivot_root 系统调用封装(glibc 没有提供封装函数) */
|
||
static int pivot_root(const char *new_root, const char *put_old)
|
||
{
|
||
return syscall(SYS_pivot_root, new_root, put_old);
|
||
}
|
||
|
||
int mounts(struct child_config *config)
|
||
{
|
||
/* 第一步:将当前所有挂载点标记为 MS_PRIVATE
|
||
* 防止 mount namespace 内的挂载事件传播到宿主机 */
|
||
fprintf(stderr, "=> remounting everything with MS_PRIVATE...");
|
||
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) {
|
||
fprintf(stderr, "failed! %m\n");
|
||
return -1;
|
||
}
|
||
fprintf(stderr, "remounted.\n");
|
||
|
||
/* 第二步:创建临时目录,将 rootfs bind mount 到这里 */
|
||
fprintf(stderr, "=> making a temp directory and a bind mount there...");
|
||
char mount_dir[] = "/tmp/tmp.XXXXXX";
|
||
if (!mkdtemp(mount_dir)) {
|
||
fprintf(stderr, "failed making a directory!\n");
|
||
return -1;
|
||
}
|
||
|
||
/* bind mount:将 -m 指定的 rootfs 挂载到临时目录 */
|
||
if (mount(config->mount_dir, mount_dir, NULL, MS_BIND | MS_PRIVATE, NULL)) {
|
||
fprintf(stderr, "bind mount failed!\n");
|
||
return -1;
|
||
}
|
||
|
||
/* 在挂载点内创建 oldroot 子目录,用于存放旧根 */
|
||
char inner_mount_dir[] = "/tmp/tmp.XXXXXX/oldroot.XXXXXX";
|
||
memcpy(inner_mount_dir, mount_dir, sizeof(mount_dir) - 1);
|
||
if (!mkdtemp(inner_mount_dir)) {
|
||
fprintf(stderr, "failed making the inner directory!\n");
|
||
return -1;
|
||
}
|
||
fprintf(stderr, "done.\n");
|
||
|
||
/* 第三步:pivot_root,将根文件系统切换到 rootfs */
|
||
fprintf(stderr, "=> pivoting root...");
|
||
if (pivot_root(mount_dir, inner_mount_dir)) {
|
||
fprintf(stderr, "failed!\n");
|
||
return -1;
|
||
}
|
||
fprintf(stderr, "done.\n");
|
||
|
||
/* 第四步:卸载旧根文件系统,彻底与宿主机文件系统断开 */
|
||
char *old_root_dir = basename(inner_mount_dir);
|
||
char old_root[sizeof(inner_mount_dir) + 1] = { "/" };
|
||
strcpy(&old_root[1], old_root_dir);
|
||
|
||
fprintf(stderr, "=> unmounting %s...", old_root);
|
||
if (chdir("/")) {
|
||
fprintf(stderr, "chdir failed! %m\n");
|
||
return -1;
|
||
}
|
||
/* MNT_DETACH:lazy unmount,先断开挂载点,等引用释放后再清理 */
|
||
if (umount2(old_root, MNT_DETACH)) {
|
||
fprintf(stderr, "umount failed! %m\n");
|
||
return -1;
|
||
}
|
||
if (rmdir(old_root)) {
|
||
fprintf(stderr, "rmdir failed! %m\n");
|
||
return -1;
|
||
}
|
||
fprintf(stderr, "done.\n");
|
||
|
||
/* 第五步:挂载 /proc,让 ps、top 等工具能读取容器内进程信息 */
|
||
fprintf(stderr, "=> mounting /proc...");
|
||
if (mount("proc", "/proc", "proc", 0, NULL)) {
|
||
fprintf(stderr, "failed: %m\n");
|
||
return -1;
|
||
}
|
||
fprintf(stderr, "done.\n");
|
||
|
||
/* 第六步:挂载 /dev(tmpfs),创建必要的设备节点 */
|
||
fprintf(stderr, "=> mounting /dev...");
|
||
if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,size=65536k")) {
|
||
fprintf(stderr, "failed: %m\n");
|
||
return -1;
|
||
}
|
||
/* 清除 umask,确保设备节点权限正确(0666) */
|
||
umask(0);
|
||
/* 创建基本设备节点:null、zero、urandom */
|
||
if (mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3))) {
|
||
fprintf(stderr, "mknod null failed: %m\n");
|
||
}
|
||
if (mknod("/dev/zero", S_IFCHR | 0666, makedev(1, 5))) {
|
||
fprintf(stderr, "mknod zero failed: %m\n");
|
||
}
|
||
if (mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9))) {
|
||
fprintf(stderr, "mknod urandom failed: %m\n");
|
||
}
|
||
fprintf(stderr, "done.\n");
|
||
|
||
return 0;
|
||
}
|