/* 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; }