Permalink
Browse files

startup: pivot into a mostly-empty new root

And umount everything but /proc and /run/cgmanager/fs in our new root.

That prevents us from pinning them if the host wants to unmount them.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
  • Loading branch information...
1 parent 2eddc60 commit a08d1c038c8457cda1b5d85c4d628595157812c1 @hallyn hallyn committed Feb 11, 2015
Showing with 141 additions and 2 deletions.
  1. +2 −0 configure.ac
  2. +139 −2 fs.c
View
@@ -25,6 +25,8 @@ AC_PROG_CC_C99
AC_PATH_PROG([NIH_DBUS_TOOL], [nih-dbus-tool])
+AC_CHECK_FUNCS([setns pivot_root])
+
PKG_CHECK_MODULES([NIH], [libnih >= 1.0.3])
PKG_CHECK_MODULES([NIH_DBUS], [libnih-dbus >= 1.0.0])
PKG_CHECK_MODULES([DBUS], [dbus-1 >= 1.2.16])
View
141 fs.c
@@ -56,6 +56,21 @@
#define AGENT SBINDIR "/cgm-release-agent"
#define AGENT_LINK_PATH "/run/cgmanager/agents"
+/* Define pivot_root() if missing from the C library */
+#ifndef HAVE_PIVOT_ROOT
+static int pivot_root(const char * new_root, const char * put_old)
+{
+#ifdef __NR_pivot_root
+return syscall(__NR_pivot_root, new_root, put_old);
+#else
+errno = ENOSYS;
+return -1;
+#endif
+}
+#else
+extern int pivot_root(const char * new_root, const char * put_old);
+#endif
+
char *all_controllers;
struct controller_mounts {
@@ -125,6 +140,10 @@ bool setup_base_run_path(void)
nih_fatal("%s: failed to create /run/cgmanager/fs", __func__);
return false;
}
+ if (mount("cgmfs", "/run/cgmanager/fs", "tmpfs", 0, "size=100000,mode=0755") < 0) {
+ nih_fatal("%s: failed to mount tmpfs onto /run/cgmanager/fs", __func__);
+ return false;
+ }
if (mkdir(AGENT_LINK_PATH, 0755) < 0 && errno != EEXIST) {
nih_fatal("%s: failed to create %s", __func__, AGENT_LINK_PATH);
return false;
@@ -806,6 +825,116 @@ int collect_subsystems(char *extra_mounts, char *skip_mounts)
return 0;
}
+#define NEWROOT "/run/cgmanager/root"
+
+static int do_pivot(void) {
+ int oldroot = -1, newroot = -1;
+
+ oldroot = open("/", O_DIRECTORY | O_RDONLY);
+ if (oldroot < 0) {
+ nih_fatal("%s: Error opening old-/ for fchdir", __func__);
+ return -1;
+ }
+ newroot = open(NEWROOT, O_DIRECTORY | O_RDONLY);
+ if (newroot < 0) {
+ nih_fatal("%s: Error opening new-/ for fchdir", __func__);
+ goto fail;
+ }
+
+ /* change into new root fs */
+ if (fchdir(newroot)) {
+ nih_fatal("%s: can't chdir to new rootfs '%s'", __func__, NEWROOT);
+ goto fail;
+ }
+
+ /* pivot_root into our new root fs */
+ if (pivot_root(".", ".")) {
+ nih_fatal("%s: pivot_root syscall failed: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * at this point the old-root is mounted on top of our new-root
+ * To unmounted it we must not be chdir'd into it, so escape back
+ * to old-root
+ */
+ if (fchdir(oldroot) < 0) {
+ nih_fatal("%s: Error entering oldroot", __func__);
+ goto fail;
+ }
+ if (umount2(".", MNT_DETACH) < 0) {
+ nih_fatal("%s: Error detaching old root", __func__);
+ goto fail;
+ }
+
+ if (fchdir(newroot) < 0) {
+ nih_fatal("%s: Error re-entering newroot", __func__);
+ goto fail;
+ }
+
+ close(oldroot);
+ close(newroot);
+
+ return 0;
+
+fail:
+ if (oldroot != -1)
+ close(oldroot);
+ if (newroot != -1)
+ close(newroot);
+ return -1;
+}
+
+static int pivot_into_new_root(void) {
+ int i, ret;
+ char *createdirs[] = {NEWROOT "/proc", NEWROOT "/run",
+ NEWROOT "/run/cgmanager", NEWROOT "/run/cgmanager/fs", NULL};
+ char path[100];
+
+ /* Mount tmpfs for new root */
+ if (mkdir(NEWROOT, 0755) < 0 && errno != EEXIST) {
+ nih_fatal("%s: Failed to create directory for new root\n", __func__);
+ return -1;
+ }
+ ret = mount("root", NEWROOT, "tmpfs", 0, "size=10000,mode=0755");
+ if (ret < 0) {
+ nih_fatal("%s: Failed to mount tmpfs for root", __func__);
+ return -1;
+ }
+
+ /* create /proc and /run/cgmanager/fs, and move-mount those */
+ for (i = 0; createdirs[i]; i++) {
+ if (mkdir(createdirs[i], 0755) < 0) {
+ nih_fatal("%s: failed to created %s\n", __func__, createdirs[i]);
+ return -1;
+ }
+ }
+
+ ret = snprintf(path, 100, NEWROOT "/proc");
+ if (ret < 0 || ret > 100)
+ return -1;
+ if (mount("/proc", path, NULL, MS_REC|MS_MOVE, 0) < 0) {
+ nih_fatal("%s: failed to move /proc into new root: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+ ret = snprintf(path, 100, NEWROOT "/run/cgmanager/fs");
+ if (ret < 0 || ret > 100)
+ return -1;
+ if (mount("/run/cgmanager/fs", path, NULL, MS_REC|MS_MOVE, 0) < 0) {
+ nih_fatal("%s: failed to move /run/cgmanager/fs into new root: %s",
+ __func__, strerror(errno));
+ return -1;
+ }
+
+ /* Pivot into new root */
+ if (do_pivot() < 0)
+ return -1;
+
+ return 0;
+}
+
/**
* Mount the cgroup filesystems and record the information.
* This should take configuration data from /etc. For now,
@@ -827,8 +956,10 @@ int setup_cgroup_mounts(void)
return 0;
}
- if (mount(NULL, "/", NULL, MS_REC|MS_SLAVE, 0) < 0)
- nih_warn("Failed to re-mount / non-shared");
+ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, 0) < 0) {
+ nih_warn("Failed to re-mount / private");
+ return -1;
+ }
/*
* Mount a tmpfs on top of /root in case / is still ro when we
@@ -847,6 +978,12 @@ int setup_cgroup_mounts(void)
}
}
+ /* Now pivot into a new root */
+ if (pivot_into_new_root() < 0) {
+ nih_fatal("Failed pivoting into new root");
+ return -1;
+ }
+
return 0;
}

0 comments on commit a08d1c0

Please sign in to comment.