+ int i;
+
+ f = fopen("/proc/self/mountinfo", "r");
+ if (!f)
+ return false;
+
+ while (getline(&line, &len, f) != -1) {
+ for (p = line, i = 0; p && i < 4; i++)
+ p = strchr(p + 1, ' ');
+ if (!p)
+ continue;
+ p2 = strchr(p + 1, ' ');
+ if (!p2)
+ continue;
+ *p2 = '\0';
+ if (strcmp(p + 1, "/") == 0) {
+ // this is '/'. is it the ramfs?
+ p = strchr(p2 + 1, '-');
+ if (p && strncmp(p, "- rootfs rootfs ", 16) == 0) {
+ free(line);
+ fclose(f);
+ return true;
+ }
+ }
+ }
+ free(line);
+ fclose(f);
+ return false;
+}
+
+static int pivot_enter()
+{
+ int ret = -1, oldroot = -1, newroot = -1;
+
+ oldroot = open("/", O_DIRECTORY | O_RDONLY);
+ if (oldroot < 0) {
+ lxcfs_error("%s\n", "Failed to open old root for fchdir.");
+ return ret;
+ }
+
+ newroot = open(ROOTDIR, O_DIRECTORY | O_RDONLY);
+ if (newroot < 0) {
+ lxcfs_error("%s\n", "Failed to open new root for fchdir.");
+ goto err;
+ }
+
+ /* change into new root fs */
+ if (fchdir(newroot) < 0) {
+ lxcfs_error("Failed to change directory to new rootfs: %s.\n", ROOTDIR);
+ goto err;
+ }
+
+ /* pivot_root into our new root fs */
+ if (pivot_root(".", ".") < 0) {
+ lxcfs_error("pivot_root() syscall failed: %s.\n", strerror(errno));
+ goto err;
+ }
+
+ /*
+ * 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 the old-root.
+ */
+ if (fchdir(oldroot) < 0) {
+ lxcfs_error("%s\n", "Failed to enter old root.");
+ goto err;
+ }
+
+ if (umount2(".", MNT_DETACH) < 0) {
+ lxcfs_error("%s\n", "Failed to detach old root.");
+ goto err;
+ }
+
+ if (fchdir(newroot) < 0) {
+ lxcfs_error("%s\n", "Failed to re-enter new root.");
+ goto err;
+ }
+
+ ret = 0;
+
+err:
+ if (oldroot > 0)
+ close(oldroot);
+ if (newroot > 0)
+ close(newroot);
+
+ return ret;
+}
+
+static int chroot_enter()
+{
+ if (mount(ROOTDIR, "/", NULL, MS_REC | MS_BIND, NULL)) {
+ lxcfs_error("Failed to recursively bind-mount %s into /.", ROOTDIR);
+ return -1;
+ }
+
+ if (chroot(".") < 0) {
+ lxcfs_error("Call to chroot() failed: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ if (chdir("/") < 0) {
+ lxcfs_error("Failed to change directory: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int permute_and_enter(void)
+{
+ struct statfs sb;
+
+ if (statfs("/", &sb) < 0) {
+ lxcfs_error("%s\n", "Could not stat / mountpoint.");
+ return -1;
+ }
+
+ /* has_fs_type() is not reliable. When the ramfs is a tmpfs it will
+ * likely report TMPFS_MAGIC. Hence, when it reports no we still check
+ * /proc/1/mountinfo. */
+ if (has_fs_type(&sb, RAMFS_MAGIC) || is_on_ramfs())
+ return chroot_enter();
+
+ if (pivot_enter() < 0) {
+ lxcfs_error("%s\n", "Could not perform pivot root.");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Prepare our new clean root. */
+static int permute_prepare(void)
+{
+ if (mkdir(ROOTDIR, 0700) < 0 && errno != EEXIST) {
+ lxcfs_error("%s\n", "Failed to create directory for new root.");
+ return -1;
+ }
+
+ if (mount("/", ROOTDIR, NULL, MS_BIND, 0) < 0) {
+ lxcfs_error("Failed to bind-mount / for new root: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ if (mount(RUNTIME_PATH, ROOTDIR RUNTIME_PATH, NULL, MS_BIND, 0) < 0) {
+ lxcfs_error("Failed to bind-mount /run into new root: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ if (mount(BASEDIR, ROOTDIR BASEDIR, NULL, MS_REC | MS_MOVE, 0) < 0) {
+ printf("Failed to move " BASEDIR " into new root: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Calls chroot() on ramfs, pivot_root() in all other cases. */
+static bool permute_root(void)
+{
+ /* Prepare new root. */
+ if (permute_prepare() < 0)
+ return false;
+
+ /* Pivot into new root. */
+ if (permute_and_enter() < 0)
+ return false;
+
+ return true;
+}
+
+static bool cgfs_prepare_mounts(void)
+{
+ if (!mkdir_p(BASEDIR, 0700)) {
+ lxcfs_error("%s\n", "Failed to create lxcfs cgroup mountpoint.");
+ return false;
+ }
+
+ if (!umount_if_mounted()) {
+ lxcfs_error("%s\n", "Failed to clean up old lxcfs cgroup mountpoint.");
+ return false;
+ }
+
+ if (unshare(CLONE_NEWNS) < 0) {
+ lxcfs_error("Failed to unshare mount namespace: %s.\n", strerror(errno));
+ return false;
+ }
+
+ if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0) < 0) {
+ lxcfs_error("Failed to remount / private: %s.\n", strerror(errno));
+ return false;
+ }
+
+ if (mount("tmpfs", BASEDIR, "tmpfs", 0, "size=100000,mode=700") < 0) {
+ lxcfs_error("%s\n", "Failed to mount tmpfs over lxcfs cgroup mountpoint.");
+ return false;
+ }
+
+ return true;
+}
+
+static bool cgfs_mount_hierarchies(void)
+{
+ char *target;
+ size_t clen, len;
+ int i, ret;
+
+ for (i = 0; i < num_hierarchies; i++) {
+ char *controller = hierarchies[i];
+
+ clen = strlen(controller);
+ len = strlen(BASEDIR) + clen + 2;
+ target = malloc(len);
+ if (!target)
+ return false;
+
+ ret = snprintf(target, len, "%s/%s", BASEDIR, controller);
+ if (ret < 0 || ret >= len) {
+ free(target);
+ return false;
+ }
+ if (mkdir(target, 0755) < 0 && errno != EEXIST) {
+ free(target);
+ return false;
+ }
+ if (!strcmp(controller, "unified"))
+ ret = mount("none", target, "cgroup2", 0, NULL);
+ else
+ ret = mount(controller, target, "cgroup", 0, controller);
+ if (ret < 0) {
+ lxcfs_error("Failed mounting cgroup %s: %s\n", controller, strerror(errno));
+ free(target);
+ return false;
+ }
+
+ fd_hierarchies[i] = open(target, O_DIRECTORY);
+ if (fd_hierarchies[i] < 0) {
+ free(target);
+ return false;
+ }
+ free(target);
+ }
+ return true;
+}
+
+static bool cgfs_setup_controllers(void)
+{
+ if (!cgfs_prepare_mounts())
+ return false;
+
+ if (!cgfs_mount_hierarchies()) {
+ lxcfs_error("%s\n", "Failed to set up private lxcfs cgroup mounts.");
+ return false;
+ }
+
+ if (!permute_root())
+ return false;
+
+ return true;
+}
+
+static int preserve_ns(int pid)
+{
+ int ret;
+ size_t len = 5 /* /proc */ + 21 /* /int_as_str */ + 7 /* /ns/mnt */ + 1 /* \0 */;
+ char path[len];
+
+ ret = snprintf(path, len, "/proc/%d/ns/mnt", pid);
+ if (ret < 0 || (size_t)ret >= len)
+ return -1;
+
+ return open(path, O_RDONLY | O_CLOEXEC);
+}
+
+static void __attribute__((constructor)) collect_and_mount_subsystems(void)
+{
+ FILE *f;
+ char *cret, *line = NULL;
+ char cwd[MAXPATHLEN];
+ size_t len = 0;
+ int i, init_ns = -1;
+ bool found_unified = false;