Exemplo n.º 1
0
void copy_nodes(const char *dest) {
	int rc;

	mode_t u = umask(0000);

	const char *nodes[] = {
		"/dev/tty",
		"/dev/full",
		"/dev/null",
		"/dev/zero",
		"/dev/random",
		"/dev/urandom"
	};

	for (size_t i = 0; i <  sizeof(nodes) / sizeof(*nodes); i++) {
		struct stat sb;
		_free_ char *target = NULL;

		rc = asprintf(&target, "%s%s", dest, nodes[i]);
		if (rc < 0) fail_printf("OOM");

		rc = stat(nodes[i], &sb);
		if (rc < 0) sysf_printf("stat()");

		rc = mknod(target, sb.st_mode, sb.st_rdev);
		if (rc < 0) sysf_printf("mknod()");
	}

	umask(u);
}
Exemplo n.º 2
0
static void do_chroot(const char *dest) {
	int rc;

	rc = chdir(dest);
	if (rc < 0) sysf_printf("chdir()");

	rc = chroot(".");
	if (rc < 0) sysf_printf("chroot()");

	rc = chdir("/");
	if (rc < 0) sysf_printf("chdir(/)");
}
Exemplo n.º 3
0
void make_symlinks(const char *dest) {
	int rc;

	const char *src[] = {
		"/proc/kcore",
		"/proc/self/fd",
		"/proc/self/fd/0",
		"/proc/self/fd/1",
		"/proc/self/fd/2"
	};

	const char *dst[] = {
		"/dev/core",
		"/dev/fd",
		"/dev/stdin",
		"/dev/stdout",
		"/dev/stderr"
	};

	for (size_t i = 0; i <  sizeof(src) / sizeof(*src); i++) {
		_free_ char *link = NULL;

		rc = asprintf(&link, "%s/%s", dest, dst[i]);
		if (rc < 0) fail_printf("OOM");

		rc = symlink(src[i], link);
		if (rc < 0) sysf_printf("symlink()");
	}
}
Exemplo n.º 4
0
static void do_daemonize(void) {
	int rc;

	openlog("pflask", LOG_NDELAY | LOG_PID, LOG_DAEMON);
	use_syslog = 1;

	rc = daemon(0, 0);
	if (rc < 0) sysf_printf("daemon()");
}
Exemplo n.º 5
0
void make_ptmx(const char *dest) {
	int rc;

	_free_ char *target = NULL;

	rc = asprintf(&target, "%s/dev/ptmx", dest);
	if (rc < 0) fail_printf("OOM");

	rc = symlink("/dev/pts/ptmx", target);
	if (rc < 0) sysf_printf("symlink()");
}
Exemplo n.º 6
0
void make_console(const char *dest, const char *console) {
	int rc;

	struct stat sb;
	_free_ char *target = NULL;

	rc = chmod(console, 0600);
	if (rc < 0) sysf_printf("chmod()");

	rc = chown(console, 0, 0);
	if (rc < 0) sysf_printf("chown()");

	rc = stat(console, &sb);
	if (rc < 0) sysf_printf("stat()");

	rc = asprintf(&target, "%s/dev/console", dest);
	if (rc < 0) fail_printf("OOM");

	rc = mknod(target, sb.st_mode, sb.st_rdev);
	if (rc < 0) sysf_printf("mknod()");

	rc = mount(console, target, NULL, MS_BIND, NULL);
	if (rc < 0) sysf_printf("mount()");
}
Exemplo n.º 7
0
static pid_t do_clone(void) {
	pid_t pid;

	pid = syscall(__NR_clone, clone_flags, NULL);
	if (pid < 0) {
		if (errno == EINVAL) {
			clone_flags &= ~(CLONE_NEWUSER);
			pid = syscall(__NR_clone, clone_flags, NULL);
		}
	}

	if (pid < 0) sysf_printf("clone()");

	return pid;
}
Exemplo n.º 8
0
void add_mount_from_spec(const char *spec) {
	int rc;

	size_t c;

	_free_ char **opts = NULL;

	_free_ char *tmp = strdup(spec);
	if (tmp == NULL) fail_printf("OOM");

	c = split_str(tmp, &opts, ",");
	if (c == 0) fail_printf("Invalid mount spec '%s'", spec);

	if (strncmp(opts[0], "bind", 4) == 0) {
		_free_ char *src = NULL;
		_free_ char *dst = NULL;

		if (c < 3) fail_printf("Invalid mount spec '%s'", spec);

		src = realpath(opts[1], NULL);
		if (src == NULL) sysf_printf("realpath()");

		dst = realpath(opts[2], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		add_mount(&mounts, src, dst, "bind", MS_BIND, NULL);

		if (strncmp(opts[0], "bind-ro", 8) == 0)
			add_mount(&mounts, src, dst, "bind-ro",
			          MS_REMOUNT | MS_BIND | MS_RDONLY, NULL);
	} else if (strncmp(opts[0], "aufs", 5) == 0) {
		_free_ char *dst = NULL;
		_free_ char *overlay = NULL;
		_free_ char *aufs_opts = NULL;

		if (c < 3) fail_printf("Invalid mount spec '%s'", spec);

		overlay = realpath(opts[1], NULL);
		if (overlay == NULL) sysf_printf("realpath()");

		dst = realpath(opts[2], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		rc = asprintf(&aufs_opts, "br:%s=rw:%s=ro", overlay, dst);
		if (rc < 0) fail_printf("OOM");

		add_mount(&mounts, NULL, dst, "aufs", 0, aufs_opts);
	} else if (strncmp(opts[0], "overlay", 8) == 0) {
		_free_ char *dst = NULL;
		_free_ char *overlay = NULL;
		_free_ char *workdir = NULL;

		if (c < 4) fail_printf("Invalid mount spec '%s'", spec);

		overlay = realpath(opts[1], NULL);
		if (overlay == NULL) sysf_printf("realpath()");

		dst = realpath(opts[2], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		workdir = realpath(opts[3], NULL);
		if (workdir == NULL) sysf_printf("realpath()");

		add_overlay_mount(&mounts, overlay, dst, workdir);
	} else if (strncmp(opts[0], "tmp", 4) == 0) {
		_free_ char *dst = NULL;

		if (c < 2) fail_printf("Invalid mount spec '%s'", spec);

		dst = realpath(opts[1], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		add_mount(&mounts, "tmpfs", dst, "tmpfs", 0, NULL);
	} else {
		fail_printf("Invalid mount type '%s'", opts[0]);
	}
}
Exemplo n.º 9
0
int main(int argc, char *argv[]) {
    int rc, sync[2];

    pid_t pid = -1;

    siginfo_t status;

    struct mount *mounts = NULL;
    struct netif *netifs = NULL;
    struct cgroup *cgroups = NULL;
    struct user *users = NULL;
#if HAVE_LIBCAP_NG
    struct capability *caps = NULL;
#endif

    char *master;
    _close_ int master_fd = -1;

    char ephemeral_dir[] = "/tmp/pflask-ephemeral-XXXXXX";

    int clone_flags = CLONE_NEWNS  |
                          CLONE_NEWIPC |
                          CLONE_NEWPID |
#ifdef CLONE_NEWCGROUP
                          CLONE_NEWCGROUP |
#endif
                          CLONE_NEWUTS;

    struct gengetopt_args_info args;

    if (cmdline_parser(argc, argv, &args) != 0)
        return 1;

    for (unsigned int i = 0; i < args.mount_given; i++) {
        validate_optlist("--mount", args.mount_arg[i]);
        mount_add_from_spec(&mounts, args.mount_arg[i]);
    }

    for (unsigned int i = 0; i < args.netif_given; i++) {
        clone_flags |= CLONE_NEWNET;

        if (args.netif_arg != NULL) {
            validate_optlist("--netif", args.netif_arg[i]);
            netif_add_from_spec(&netifs, args.netif_arg[i]);
        }
    }

    if (args.user_given && !args.user_map_given) {
        uid_t uid;
        gid_t gid;

        clone_flags |= CLONE_NEWUSER;

        if (user_get_uid_gid(args.user_arg, &uid, &gid)) {
            user_add_map(&users, 'u', uid, uid, 1);
            user_add_map(&users, 'g', gid, gid, 1);
        }
    }

    for (unsigned int i = 0; i < args.user_map_given; i++) {
        size_t count;
        uid_t id, host_id;

        char *start = args.user_map_arg[i], *end = NULL;

        validate_optlist("--user-map", args.user_map_arg[i]);

        clone_flags |= CLONE_NEWUSER;

        id = strtoul(start, &end, 10);
        if (*end != ':')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        start = end + 1;

        host_id = strtoul(start, &end, 10);
        if (*end != ':')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        start = end + 1;

        count = strtoul(start, &end, 10);
        if (*end != '\0')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        user_add_map(&users, 'u', id, host_id, count);
        user_add_map(&users, 'g', id, host_id, count);
    }

    for (unsigned int i = 0; i < args.cgroup_given; i++)
        cgroup_add(&cgroups, args.cgroup_arg[i]);

#if HAVE_LIBCAP_NG
    for (unsigned int i = 0; i < args.caps_given; i++)
        capability_add(&caps, args.caps_arg[i]);
#endif

    if (args.no_userns_flag)
        clone_flags &= ~(CLONE_NEWUSER);

    if (args.no_mountns_flag)
        clone_flags &= ~(CLONE_NEWNS);

    if (args.no_netns_flag)
        clone_flags &= ~(CLONE_NEWNET);

    if (args.no_ipcns_flag)
        clone_flags &= ~(CLONE_NEWIPC);

    if (args.no_utsns_flag)
        clone_flags &= ~(CLONE_NEWUTS);

    if (args.no_pidns_flag)
        clone_flags &= ~(CLONE_NEWPID);

    if (args.attach_given) {
        master_fd = recv_pty(args.attach_arg);
        fail_if(master_fd < 0, "Invalid PID '%u'", args.attach_arg);

        process_pty(master_fd);
        return 0;
    }

    open_master_pty(&master_fd, &master);

    if (args.detach_flag)
        do_daemonize();

    sync_init(sync);

    if (args.ephemeral_flag) {
        if (!mkdtemp(ephemeral_dir))
            sysf_printf("mkdtemp()");
    }

    pid = do_clone(&clone_flags);

    if (!pid) {
        closep(&master_fd);

        rc = prctl(PR_SET_PDEATHSIG, SIGKILL);
        sys_fail_if(rc < 0, "prctl(PR_SET_PDEATHSIG)");

        rc = setsid();
        sys_fail_if(rc < 0, "setsid()");

        sync_barrier_parent(sync, SYNC_START);

        sync_close(sync);

        open_slave_pty(master);

        setup_user(args.user_arg);

        if (args.hostname_given) {
            rc = sethostname(args.hostname_arg,
                             strlen(args.hostname_arg));
            sys_fail_if(rc < 0, "Error setting hostname");
        }

        setup_mount(mounts, args.chroot_arg, args.ephemeral_flag ?
                                               ephemeral_dir : NULL);

        if (args.chroot_given) {
            setup_nodes(args.chroot_arg);

            setup_ptmx(args.chroot_arg);

            setup_symlinks(args.chroot_arg);

            setup_console(args.chroot_arg, master);

            do_chroot(args.chroot_arg);
        }

        if (clone_flags & CLONE_NEWNET)
            config_netif();

        umask(0022);

#if HAVE_LIBCAP_NG
        setup_capabilities(caps);
#endif

        if (args.chdir_given) {
            rc = chdir(args.chdir_arg);
            sys_fail_if(rc < 0, "Error changing cwd");
        }

        if (args.chroot_given) {
            char *term = getenv("TERM");

            if (!args.keepenv_flag)
                clearenv();

            setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
            setenv("USER", args.user_arg, 1);
            setenv("LOGNAME", args.user_arg, 1);
            if (term)
                setenv("TERM", term, 1);
        }

        for (unsigned int i = 0; i < args.setenv_given; i++) {
            rc = putenv(strdup(args.setenv_arg[i]));
            sys_fail_if(rc != 0, "Error setting environment");
        }

        setenv("container", "pflask", 1);

        if (argc > optind)
            rc = execvpe(argv[optind], argv + optind, environ);
        else
            rc = execle("/bin/bash", "-bash", NULL, environ);

        sys_fail_if(rc < 0, "Error executing command");
    }

    sync_wait_child(sync, SYNC_START);

    if (args.chroot_given && (clone_flags & CLONE_NEWUSER))
        setup_console_owner(master, users);

    setup_cgroup(cgroups, pid);

    setup_netif(netifs, pid);

#ifdef HAVE_DBUS
    register_machine(pid, args.chroot_given ? args.chroot_arg : "");
#endif

    if (clone_flags & CLONE_NEWUSER)
        setup_user_map(users, pid);

    sync_wake_child(sync, SYNC_DONE);

    sync_close(sync);

    if (args.detach_flag)
        serve_pty(master_fd);
    else
        process_pty(master_fd);

    kill(pid, SIGKILL);

    rc = waitid(P_PID, pid, &status, WEXITED);
    sys_fail_if(rc < 0, "Error waiting for child");

    switch (status.si_code) {
    case CLD_EXITED:
        if (status.si_status != 0)
            err_printf("Child failed with code '%d'",
                       status.si_status);
        else
            ok_printf("Child exited");
        break;

    case CLD_KILLED:
        err_printf("Child was terminated by signal '%d'",
                   status.si_status);
        break;

    default:
        err_printf("Child failed");
        break;
    }

    sync_close(sync);

    clean_cgroup(cgroups);

    if (args.ephemeral_flag) {
        rc = rmdir(ephemeral_dir);
        sys_fail_if(rc != 0, "Error deleting ephemeral directory: %s",
                             ephemeral_dir);
    }

    cmdline_parser_free(&args);

    return status.si_status;
}
Exemplo n.º 10
0
int main(int argc, char *argv[]) {
	int rc, i;

	pid_t pid  = -1;
	pid_t ppid = getpid();

	uid_t uid = -1;
	gid_t gid = -1;

	_free_ char *user   = NULL;
	_free_ char *dest   = NULL;
	_free_ char *change = NULL;
	_free_ char *env    = NULL;
	_free_ char *cgroup = NULL;

	_close_ int master_fd = -1;

	char *master_name;

	int detach  = 0;
	int keepenv = 0;

	siginfo_t status;

	while ((rc = getopt_long(argc, argv, short_opts, long_opts, &i)) !=-1) {
		switch (rc) {
			case 'm':
				validate_optlist("--mount", optarg);

				add_mount_from_spec(optarg);
				break;

			case 'n':
				clone_flags |= CLONE_NEWNET;

				if (optarg != NULL) {
					validate_optlist("--netif", optarg);

					add_netif_from_spec(optarg);
				}
				break;

			case 'u':
				clone_flags |= CLONE_NEWUSER;

				freep(&user);

				user = strdup(optarg);
				break;

			case 'r':
				freep(&dest);

				dest = realpath(optarg, NULL);
				if (dest == NULL) sysf_printf("realpath()");
				break;

			case 'c':
				freep(&change);

				change = strdup(optarg);
				break;

			case 'g':
				validate_optlist("--cgroup", optarg);
				validate_cgroup_spec(optarg);

				freep(&change);

				cgroup = strdup(optarg);
				break;

			case 'd':
				detach = 1;
				break;

			case 'a': {
				char *end = NULL;
				pid = strtol(optarg, &end, 10);
				if (*end != '\0')
					fail_printf("Invalid value '%s' for --attach", optarg);
				break;
			}

			case 's': {
				validate_optlist("--setenv", optarg);

				if (env != NULL) {
					char *tmp = env;

					rc = asprintf(&env, "%s,%s", env, optarg);
					if (rc < 0) fail_printf("OOM");

					freep(&tmp);
				} else {
					env = strdup(optarg);
				}

				break;
			}

			case 'k':
				keepenv = 1;
				break;

			case 'U':
				clone_flags &= ~(CLONE_NEWUSER);
				break;

			case 'M':
				clone_flags &= ~(CLONE_NEWNS);
				break;

			case 'N':
				clone_flags &= ~(CLONE_NEWNET);
				break;

			case 'I':
				clone_flags &= ~(CLONE_NEWIPC);
				break;

			case 'H':
				clone_flags &= ~(CLONE_NEWUTS);
				break;

			case 'P':
				clone_flags &= ~(CLONE_NEWPID);
				break;

			case '?':
			case 'h':
				help();
				return 0;
		}
	}

	if (pid != -1) {
		master_fd = recv_pty(pid);
		if (master_fd < 0) fail_printf("Invalid PID '%u'", pid);

		pid = -1;
		goto process_fd;
	}

	if (user == NULL) {
		user = strdup("root");
		if (user == NULL) fail_printf("OOM");
	}

	open_master_pty(&master_fd, &master_name);

	uid = getuid();
	gid = getgid();

	if (detach == 1)
		do_daemonize();

	pid = do_clone();

	if (pid == 0) {
		closep(&master_fd);

		open_slave_pty(master_name);

		rc = setsid();
		if (rc < 0) sysf_printf("setsid()");

		rc = prctl(PR_SET_PDEATHSIG, SIGKILL);
		if (rc < 0) sysf_printf("prctl(PR_SET_PDEATHSIG)");

		if (clone_flags & CLONE_NEWUSER)
			map_user_to_user(uid, gid, user);

		do_cgroup(cgroup, ppid);

		do_mount(dest);

		if (dest != NULL) {
			copy_nodes(dest);

			make_ptmx(dest);

			make_symlinks(dest);

			make_console(dest, master_name);

			do_chroot(dest);
		}

		if (clone_flags & CLONE_NEWNET)
			setup_loopback();

		umask(0022);

		/* TODO: drop capabilities */

		do_user(user);

		if (change != NULL) {
			rc = chdir(change);
			if (rc < 0) sysf_printf("chdir()");
		}

		if (dest != NULL) {
			char *term = getenv("TERM");

			if (keepenv == 0)
				clearenv();

			setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
			setenv("USER", user, 1);
			setenv("LOGNAME", user, 1);
			setenv("TERM", term, 1);
		}

		if (env != NULL) {
			size_t i, c;

			_free_ char **vars = NULL;

			_free_ char *tmp = strdup(env);
			if (tmp == NULL) fail_printf("OOM");

			c = split_str(tmp, &vars, ",");

			for (i = 0; i < c; i++) {
				rc = putenv(strdup(vars[i]));
				if (rc != 0) sysf_printf("putenv()");
			}
		}

		setenv("container", "pflask", 1);

		if (argc > optind)
			rc = execvpe(argv[optind], argv + optind, environ);
		else
			rc = execle("/bin/bash", "-bash", NULL, environ);

		if (rc < 0) sysf_printf("exec()");
	}

	do_netif(pid);

process_fd:
	if (detach == 1)
		serve_pty(master_fd);
	else
		process_pty(master_fd);

	if (pid == -1)
		return 0;

	kill(pid, SIGKILL);

	rc = waitid(P_PID, pid, &status, WEXITED);
	if (rc < 0) sysf_printf("waitid()");

	switch (status.si_code) {
		case CLD_EXITED:
			if (status.si_status != 0)
				err_printf("Child failed with code '%d'",
				           status.si_status);
			else
				ok_printf("Child exited");
			break;

		case CLD_KILLED:
			err_printf("Child was terminated");
			break;

		default:
			err_printf("Child failed");
			break;
	}

	undo_cgroup(cgroup, ppid);

	return status.si_status;
}
Exemplo n.º 11
0
void add_mount_from_spec(char *spec) {
	int rc;

	size_t c;

	_free_ char **opts = NULL;

	_free_ char *tmp = strdup(spec);
	if (tmp == NULL) fail_printf("OOM");

	c = split_str(tmp, &opts, ",");
	if (c == 0) fail_printf("Invalid mount spec '%s'", spec);

	if (strncmp(opts[0], "bind", 4) == 0) {
		_free_ char *src = NULL;
		_free_ char *dst = NULL;

		if (c < 3) fail_printf("Invalid mount spec '%s'", spec);

		src = realpath(opts[1], NULL);
		if (src == NULL) sysf_printf("realpath()");

		dst = realpath(opts[2], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		add_mount(src, dst, NULL, MS_BIND, NULL);

		if (strncmp(opts[0], "bind-ro", 8) == 0)
			add_mount(src, dst, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY, NULL);
	} else if (strncmp(opts[0], "aufs", 5) == 0) {
		_free_ char *dst = NULL;
		_free_ char *overlay = NULL;
		_free_ char *aufs_opts = NULL;

		if (c < 3) fail_printf("Invalid mount spec '%s'", spec);

		overlay = realpath(opts[1], NULL);
		if (overlay == NULL) sysf_printf("realpath()");

		dst = realpath(opts[2], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		rc = asprintf(&aufs_opts, "br:%s=rw:%s=ro", overlay, dst);
		if (rc < 0) fail_printf("OOM");

		add_mount(NULL, dst, "aufs", 0, aufs_opts);
	} else if (strncmp(opts[0], "overlay", 8) == 0) {
		_free_ char *dst = NULL;
		_free_ char *overlay = NULL;
		_free_ char *workdir = NULL;
		_free_ char *overlayfs_opts = NULL;

		if (c < 4) fail_printf("Invalid mount spec '%s'", spec);

		overlay = realpath(opts[1], NULL);
		if (overlay == NULL) sysf_printf("realpath()");

		dst = realpath(opts[2], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		workdir = realpath(opts[3], NULL);
		if (workdir == NULL) sysf_printf("realpath()");

#ifdef HAVE_AUFS
		rc = asprintf(&overlayfs_opts, "br:%s=rw:%s=ro", overlay, dst);
		if (rc < 0) fail_printf("OOM");

		add_mount(NULL, dst, "aufs", 0, overlayfs_opts);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
		rc = asprintf(&overlayfs_opts,
		              "upperdir=%s,lowerdir=%s,workdir=%s",
		              overlay, dst, workdir);
		if (rc < 0) fail_printf("OOM");

		add_mount(NULL, dst, "overlay", 0, overlayfs_opts);
#else
		fail_printf("The 'overlay' mount type is not supported");
#endif
	} else if (strncmp(opts[0], "tmp", 4) == 0) {
		_free_ char *dst = NULL;

		if (c < 2) fail_printf("Invalid mount spec '%s'", spec);

		dst = realpath(opts[1], NULL);
		if (dst == NULL) sysf_printf("realpath()");

		add_mount("tmpfs", dst, "tmpfs", 0, NULL);
	} else {
		fail_printf("Invalid mount type '%s'", opts[0]);
	}
}
Exemplo n.º 12
0
void do_mount(char *dest) {
	int rc;

	mount_list *i = NULL;

	rc = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
	if (rc < 0) sysf_printf("mount(MS_SLAVE)");

	if (dest != NULL) {
		/* add_mount(dest, dest, NULL, MS_BIND, "bind"); */

		add_mount_inside(dest, "proc", "/proc", "proc",
			MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL);

		add_mount_inside(dest, "/proc/sys", "/proc/sys", "proc/sys",
			MS_BIND, NULL);

		add_mount_inside(dest, NULL, "/proc/sys", "proc/sys-ro",
			MS_BIND | MS_RDONLY | MS_REMOUNT, NULL);

		add_mount_inside(dest, "sysfs", "/sys", "sysfs",
			MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RDONLY, NULL);

		add_mount_inside(dest, "tmpfs", "/dev", "tmpfs",
			MS_NOSUID | MS_STRICTATIME, "mode=755");

		add_mount_inside(dest, "devpts", "/dev/pts", "devpts",
			MS_NOSUID | MS_NOEXEC,
			"newinstance,ptmxmode=000,mode=620,gid=5");

		add_mount_inside(dest, "tmpfs", "/dev/shm", "tmpfs",
			MS_NOSUID | MS_STRICTATIME | MS_NODEV, "mode=1777");

		add_mount_inside(dest, "tmpfs", "/run", "tmpfs",
			MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755");

		/* add_mount(dest, "/", NULL, MS_MOVE, "move"); */
	}

	while (mounts) {
		mount_list *next = mounts->next;
		mounts->next = i;
		i = mounts;
		mounts = next;
	}

	while (i != NULL) {
		rc = mkdir(i->dst, 0755);
		if (rc < 0) {
			switch (errno) {
				struct stat sb;

				case EEXIST:
					if (!stat(i->dst, &sb) &&
					    !S_ISDIR(sb.st_mode))
						fail_printf("Not a directory");
					break;

				default:
					sysf_printf("mkdir(%s)", i->dst);
					break;
			}
		}

		rc = mount(i->src, i->dst, i->type, i->flags, i->data);
		if (rc < 0) sysf_printf("mount(%s)", i->type);

		i = i->next;
	}
}