diff --git a/base-linux/src/base/process/process.cc b/base-linux/src/base/process/process.cc index f3064c166..73c611d5c 100644 --- a/base-linux/src/base/process/process.cc +++ b/base-linux/src/base/process/process.cc @@ -58,9 +58,9 @@ Process::Process(Dataspace_capability elf_data_ds_cap, Rm_session_capability rm_session_cap, Parent_capability parent_cap, char const *name, - char *const argv[]) + char const *root) : - _pd(name), + _pd(name, root), _cpu_session_client(Cpu_session_capability()), _rm_session_client(Rm_session_capability()) { diff --git a/base-linux/src/core/include/core_linux_syscalls.h b/base-linux/src/core/include/core_linux_syscalls.h index 659ffd434..33047db86 100644 --- a/base-linux/src/core/include/core_linux_syscalls.h +++ b/base-linux/src/core/include/core_linux_syscalls.h @@ -90,6 +90,35 @@ inline int lx_create_process(int (*entry)(void *), void *stack, void *arg) } +/********************* + ** Chroot handling ** + *********************/ + +inline int lx_chroot(const char *path) +{ + return lx_syscall(SYS_chroot, path); +} + + +inline int lx_getcwd(char *dst, size_t dst_len) +{ + return lx_syscall(SYS_getcwd, dst, dst_len); +} + + +inline int lx_bindmount(char const *source, char const *target) +{ + enum { MS_BIND = 4096 }; + return lx_syscall(SYS_mount, source, target, 0, MS_BIND, 0); +} + + +inline int lx_umount(char const *target) +{ + return lx_syscall(SYS_umount, target); +} + + /******************************************** ** Communication over Unix-domain sockets ** ********************************************/ diff --git a/base-linux/src/core/include/pd_session_component.h b/base-linux/src/core/include/pd_session_component.h index ac81753c1..51929547b 100644 --- a/base-linux/src/core/include/pd_session_component.h +++ b/base-linux/src/core/include/pd_session_component.h @@ -27,10 +27,12 @@ namespace Genode { { private: - enum { LABEL_MAX_LEN = 1024 }; + enum { LABEL_MAX_LEN = 1024 }; + enum { ROOT_PATH_MAX_LEN = 512 }; unsigned long _pid; char _label[LABEL_MAX_LEN]; + char _root[ROOT_PATH_MAX_LEN]; Parent_capability _parent; Rpc_entrypoint *_ds_ep; diff --git a/base-linux/src/core/pd_session_component.cc b/base-linux/src/core/pd_session_component.cc index 7eb619f3a..d6700e1f4 100644 --- a/base-linux/src/core/pd_session_component.cc +++ b/base-linux/src/core/pd_session_component.cc @@ -26,6 +26,166 @@ using namespace Genode; +/*********************************** + ** Utilities for chroot handling ** + ***********************************/ + +enum { MAX_PATH_LEN = 256 }; + + +/** + * Return true if specified path is an existing directory + */ +static bool is_directory(char const *path) +{ + struct stat64 s; + if (lx_stat(path, &s) != 0) + return false; + + if (!(s.st_mode & S_IFDIR)) + return false; + + return true; +} + + +static bool is_path_delimiter(char c) { return c == '/'; } + + +static bool has_trailing_path_delimiter(char const *path) +{ + char last_char = 0; + for (; *path; path++) + last_char = *path; + + return is_path_delimiter(last_char); +} + + +/** + * Return number of path elements of given path + */ +static Genode::size_t num_path_elements(char const *path) +{ + Genode::size_t count = 0; + + /* + * If path starts with non-slash, the first characters belongs to a path + * element. + */ + if (*path && !is_path_delimiter(*path)) + count = 1; + + /* count slashes */ + for (; *path; path++) + if (is_path_delimiter(*path)) + count++; + + return count; +} + + +static bool leading_path_elements(char const *path, unsigned num, + char *dst, Genode::size_t dst_len) +{ + /* counter of path delimiters */ + unsigned count = 0; + unsigned i = 0; + + if (is_path_delimiter(path[0])) + num++; + + for (; path[i] && (count < num) && (i < dst_len); i++) + { + if (is_path_delimiter(path[i])) + count++; + + if (count == num) + break; + + dst[i] = path[i]; + } + + if (i + 1 < dst_len) { + dst[i] = 0; + return true; + } + + /* string is cut, append null termination anyway */ + dst[dst_len - 1] = 0; + return false; +} + + +static void mirror_path_to_chroot(char const *chroot_path, char const *path) +{ + char target_path[MAX_PATH_LEN]; + Genode::snprintf(target_path, sizeof(target_path), "%s%s", + chroot_path, path); + + /* + * Create directory hierarchy pointing to the target path except for the + * last element. The last element will be bind-mounted to refer to the + * original 'path'. + */ + for (unsigned i = 1; i <= num_path_elements(target_path); i++) + { + char buf[MAX_PATH_LEN]; + leading_path_elements(target_path, i, buf, sizeof(buf)); + + /* skip existing directories */ + if (is_directory(buf)) + continue; + + /* create new directory */ + lx_mkdir(buf, 0777); + } + + lx_umount(target_path); + + int ret = 0; + if ((ret = lx_bindmount(path, target_path))) + PERR("bind mount failed (errno=%d)", ret); +} + + +/** + * Setup content of chroot environment as prerequisite to 'execve' new + * processes within the environment. I.e., the current working directory + * containing the ROM modules must be mounted at the same location within the + * chroot environment. + */ +static bool setup_chroot_environment(char const *chroot_path) +{ + using namespace Genode; + + static char cwd_path[MAX_PATH_LEN]; + + lx_getcwd(cwd_path, sizeof(cwd_path)); + + /* + * Validate chroot path + */ + if (!is_directory(chroot_path)) { + PERR("chroot path does not point to valid directory"); + return false; + } + + if (has_trailing_path_delimiter(chroot_path)) { + PERR("chroot path has trailing slash"); + return false; + } + + /* + * Hardlink directories needed for running Genode within the chroot + * environment. + */ + mirror_path_to_chroot(chroot_path, cwd_path); + + return true; +} + + /*************** ** Utilities ** ***************/ @@ -35,7 +195,8 @@ using namespace Genode; */ struct Execve_args { - const char *filename; + char const *filename; + char const *root; char *const *argv; char *const *envp; int parent_sd; @@ -49,6 +210,26 @@ static int _exec_child(Execve_args *arg) { lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE); + /* change to chroot environment */ + if (arg->root && arg->root[0]) { + + PDBG("arg->root='%s'", arg->root); + + if (setup_chroot_environment(arg->root) == false) { + PERR("Could not setup chroot environment"); + return -1; + } + + PLOG("changing root of %s (PID %d) to %s", + arg->filename, lx_getpid(), arg->root); + + int ret = lx_chroot(arg->root); + if (ret < 0) { + PERR("Syscall chroot failed (errno %d)", ret); + return -1; + } + } + return lx_execve(arg->filename, arg->argv, arg->envp); } @@ -85,6 +266,7 @@ Pd_session_component::Pd_session_component(Rpc_entrypoint *ep, const char *args) { Arg_string::find_arg(args, "label").string(_label, sizeof(_label), ""); + Arg_string::find_arg(args, "root").string(_root, sizeof(_root), ""); } @@ -95,8 +277,7 @@ Pd_session_component::~Pd_session_component() } -int Pd_session_component::bind_thread(Thread_capability) { return -1; -} +int Pd_session_component::bind_thread(Thread_capability) { return -1; } int Pd_session_component::assign_parent(Parent_capability parent) @@ -161,7 +342,7 @@ void Pd_session_component::start(Capability binary) * Argument frame as passed to 'clone'. Because, we can only pass a single * pointer, all arguments are embedded within the 'execve_args' struct. */ - Execve_args arg = { filename.buf, argv_buf, env, _parent.dst().socket }; + Execve_args arg = { filename.buf, _root, argv_buf, env, _parent.dst().socket }; _pid = lx_create_process((int (*)(void *))_exec_child, stack + STACK_SIZE - sizeof(umword_t), &arg); diff --git a/base/include/base/child.h b/base/include/base/child.h index 94531e1c3..3b5ada522 100644 --- a/base/include/base/child.h +++ b/base/include/base/child.h @@ -93,6 +93,11 @@ namespace Genode { * transfers. */ virtual Ram_session *ref_ram_session() { return env()->ram_session(); } + + /** + * Return root path of the child's PD session (only used on Linux) + */ + virtual char const *root() const { return ""; } }; diff --git a/base/include/base/process.h b/base/include/base/process.h index 39e5ef1fe..baa2a9de1 100644 --- a/base/include/base/process.h +++ b/base/include/base/process.h @@ -58,7 +58,7 @@ namespace Genode { * \param parent parent of the new protection domain * \param name name of protection domain (can be used * in debugging) - * \param argv not used + * \param root optional chroot path (only on Linux) * * The dataspace 'elf_data_ds' can be read-only. * @@ -70,8 +70,8 @@ namespace Genode { Cpu_session_capability cpu_session, Rm_session_capability rm_session, Parent_capability parent, - const char *name, - char *const argv[]); + char const *name, + char const *root = ""); /** * Destructor diff --git a/base/include/pd_session/connection.h b/base/include/pd_session/connection.h index 6edb1567c..c93e01081 100644 --- a/base/include/pd_session/connection.h +++ b/base/include/pd_session/connection.h @@ -25,10 +25,15 @@ namespace Genode { * Constructor * * \param label session label + * \param root chroot path (only on Linux) */ - Pd_connection(const char *label = "") + Pd_connection(char const *label = "", char const *root = "") : - Connection(session("ram_quota=4K, label=\"%s\"", label)), + Connection(session("ram_quota=4K, label=\"%s\"%s%s%s", + label, + (root && root[0]) ? ", root=\"" : "", + (root && root[0]) ? root : "", + (root && root[0]) ? "\"" : "")), Pd_session_client(cap()) { } }; diff --git a/base/src/base/child/child.cc b/base/src/base/child/child.cc index 00b209dfe..85611f0ab 100644 --- a/base/src/base/child/child.cc +++ b/base/src/base/child/child.cc @@ -431,7 +431,7 @@ Child::Child(Dataspace_capability elf_ds, _parent_cap(_entrypoint->manage(this)), _policy(policy), _server(ram), - _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), 0) + _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), policy->root()) { } diff --git a/base/src/base/process/process.cc b/base/src/base/process/process.cc index 342e3b56d..c007108ac 100644 --- a/base/src/base/process/process.cc +++ b/base/src/base/process/process.cc @@ -175,9 +175,9 @@ Process::Process(Dataspace_capability elf_ds_cap, Rm_session_capability rm_session_cap, Parent_capability parent_cap, char const *name, - char *const argv[]) + char const *pd_args) : - _pd(name), + _pd(name, pd_args), _cpu_session_client(cpu_session_cap), _rm_session_client(rm_session_cap) { diff --git a/demo/include/launchpad/launchpad.h b/demo/include/launchpad/launchpad.h index e57ee80da..9f542a190 100644 --- a/demo/include/launchpad/launchpad.h +++ b/demo/include/launchpad/launchpad.h @@ -42,7 +42,7 @@ class Launchpad_child_policy : public Init::Traditional_child_policy : Init::Traditional_child_policy(name, server, parent_services, child_services, config_ds, - binary_ds, 0, 0, parent_entrypoint) + binary_ds, 0, 0, 0, parent_entrypoint) { } Genode::Service *resolve_session_request(const char *service_name, diff --git a/os/doc/init.txt b/os/doc/init.txt index 82e32b8ee..88f85ae31 100644 --- a/os/doc/init.txt +++ b/os/doc/init.txt @@ -262,6 +262,29 @@ as LOG output. To enable the verbose mode, assign the value "yes" to the 'verbose' attribute of the '' node. +Executing children in chroot environments on Linux +================================================== + +On the Linux base platform, each process started by init can be assigned to +a chroot environment by specifying the new root location as 'root' attribute +to the corresponding '' node. Root environments can be nested. The +root path of a nested init instance will be appended to the root path of +the outer instance. + +When using the chroot mechanism, core will mirror the current working +directory within the chroot environment via the a bind mount operation. This +step is needed to enable execve to obtain the ELF binary of the new process. + +In order to use the chroot mechanism when starting Genode's core as a non-root +user process, the core executable must be equipped with the 'CAP_SYS_ADMIN' and +'CAP_SYS_CHROOT' capabilities. 'CAP_SYS_ADMIN' is needed for bind mounting. +'CAP_SYS_CHROOT' is needed to perform the 'chroot' syscall: + +! sudo setcap cap_sys_admin,cap_sys_chroot=ep core + +For an example of using chroot, please refer to the 'os/run/chroot.run' script. + + Using the configuration concept ############################### diff --git a/os/include/init/child.h b/os/include/init/child.h index e9f465736..552d9e388 100644 --- a/os/include/init/child.h +++ b/os/include/init/child.h @@ -298,6 +298,31 @@ namespace Init { } } _name; + /** + * Path of the child's chroot environment (on Linux) + */ + struct Root + { + /* + * XXX dimension ROOT_PATH_LEN depending on the platform + */ + enum { ROOT_PATH_LEN = 256 }; + char path[ROOT_PATH_LEN]; + + /** + * Constructor + */ + Root(Genode::Xml_node start_node) + { + path[0] = 0; + + try { + start_node.attribute("root").value(path, sizeof(path)); } + catch (Genode::Xml_node::Nonexistent_attribute) { } + } + + } _root; + /** * Resources assigned to the child */ @@ -369,6 +394,7 @@ namespace Init { Init::Child_policy_provide_rom_file _config_policy; Init::Child_policy_provide_rom_file _binary_policy; Init::Child_policy_redirect_rom_file _configfile_policy; + Init::Child_policy_prepend_chroot_path _chroot_policy; public: @@ -385,6 +411,7 @@ namespace Init { _default_route_node(default_route_node), _name_registry(name_registry), _name(start_node, name_registry), + _root(start_node), _resources(start_node, _name.unique, prio_levels_log2), _entrypoint(cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false), _binary_rom(_name.file, _name.unique), @@ -398,7 +425,8 @@ namespace Init { _priority_policy(_resources.prio_levels_log2, _resources.priority), _config_policy("config", _config.dataspace(), &_entrypoint), _binary_policy("binary", _binary_rom.dataspace(), &_entrypoint), - _configfile_policy("config", _config.filename()) + _configfile_policy("config", _config.filename()), + _chroot_policy(_root.path) { using namespace Genode; @@ -545,9 +573,10 @@ namespace Init { void filter_session_args(const char *service, char *args, Genode::size_t args_len) { - _labeling_policy.filter_session_args(service, args, args_len); - _priority_policy.filter_session_args(service, args, args_len); + _labeling_policy. filter_session_args(service, args, args_len); + _priority_policy. filter_session_args(service, args, args_len); _configfile_policy.filter_session_args(service, args, args_len); + _chroot_policy. filter_session_args(service, args, args_len); } bool announce_service(const char *service_name, @@ -569,6 +598,8 @@ namespace Init { rs->announce(root); return true; } + + char const *root() const { return _root.path; } }; } diff --git a/os/include/init/child_policy.h b/os/include/init/child_policy.h index f644efdb8..505167aaa 100644 --- a/os/include/init/child_policy.h +++ b/os/include/init/child_policy.h @@ -63,6 +63,53 @@ namespace Init { }; + /** + * Policy for prepending the chroot path of the child + * + * This policy is effective only on the Linux base platform. + * + * By applying this policy, the chroot path of the child gets supplied + * to PD session requests. + */ + class Child_policy_prepend_chroot_path + { + private: + + char const *_root_prefix; + + public: + + Child_policy_prepend_chroot_path(const char *root_prefix) + : _root_prefix(root_prefix) { } + + /** + * Filter arguments of session request + * + * This function prepends the '_root' to the 'root' session + * argument of PD sessions initiated through the child (not the + * child's PD session). + */ + void filter_session_args(const char *session, char *args, + Genode::size_t args_len) + { + using namespace Genode; + + if (strcmp(session, "PD") != 0) + return; + + char path_buf[Parent::Session_args::MAX_SIZE]; + Arg_string::find_arg(args, "root").string(path_buf, sizeof(path_buf), ""); + + char value_buf[Parent::Session_args::MAX_SIZE]; + Genode::snprintf(value_buf, sizeof(value_buf), + "\"%s%s\"", + _root_prefix, path_buf); + + Arg_string::set_arg(args, args_len, "root", value_buf); + } + }; + + class Child_policy_handle_cpu_priorities { /* priority parameters */ @@ -237,6 +284,8 @@ namespace Init { enum { NAME_LEN = 64 }; char _name[NAME_LEN]; + char const *_root; + Genode::Server *_server; Genode::Service_registry *_parent_services; Genode::Service_registry *_child_services; @@ -260,8 +309,10 @@ namespace Init { Genode::Dataspace_capability binary_ds, long prio_levels_log2, long priority, + char const *root, Genode::Rpc_entrypoint *parent_entrypoint) : + _root(root), _server(server), _parent_services(parent_services), _child_services(child_services), @@ -270,8 +321,10 @@ namespace Init { _labeling_policy(_name), _priority_policy(prio_levels_log2, priority), _config_policy("config", config_ds, _parent_entrypoint), - _binary_policy("binary", binary_ds, _parent_entrypoint) { - Genode::strncpy(_name, name, sizeof(_name)); } + _binary_policy("binary", binary_ds, _parent_entrypoint) + { + Genode::strncpy(_name, name, sizeof(_name)); + } const char *name() const { return _name; } diff --git a/os/include/loader_session/client.h b/os/include/loader_session/client.h index 131321680..519c20dab 100644 --- a/os/include/loader_session/client.h +++ b/os/include/loader_session/client.h @@ -41,8 +41,8 @@ namespace Loader { void view_ready_sigh(Signal_context_capability sigh) { call(sigh); } - void start(Name const &binary, Name const &label = "") { - call(binary, label); } + void start(Name const &binary, Name const &label = "", Path const &root = "") { + call(binary, label, root); } Nitpicker::View_capability view() { return call(); } diff --git a/os/include/loader_session/loader_session.h b/os/include/loader_session/loader_session.h index 72b8c12a0..d1537879c 100644 --- a/os/include/loader_session/loader_session.h +++ b/os/include/loader_session/loader_session.h @@ -54,6 +54,7 @@ namespace Loader { }; typedef Genode::Rpc_in_buffer<64> Name; + typedef Genode::Rpc_in_buffer<128> Path; static const char *service_name() { return "Loader"; } @@ -121,7 +122,8 @@ namespace Loader { * \throw Rom_module_does_not_exist if the specified binary could * not obtained as ROM module */ - virtual void start(Name const &binary, Name const &label = "") = 0; + virtual void start(Name const &binary, Name const &label = "", + Path const &root = "") = 0; /** * Return first nitpicker view created by the loaded subsystem @@ -152,7 +154,7 @@ namespace Loader { GENODE_RPC(Rpc_view_ready_sigh, void, view_ready_sigh, Signal_context_capability); GENODE_RPC_THROW(Rpc_start, void, start, GENODE_TYPE_LIST(Rom_module_does_not_exist), - Name const &, Name const &); + Name const &, Name const &, Path const &); GENODE_RPC_THROW(Rpc_view, Nitpicker::View_capability, view, GENODE_TYPE_LIST(View_does_not_exist)); GENODE_RPC(Rpc_view_geometry, View_geometry, view_geometry); diff --git a/os/run/chroot.run b/os/run/chroot.run index a3b25ca77..cbb8fc5db 100644 --- a/os/run/chroot.run +++ b/os/run/chroot.run @@ -10,7 +10,7 @@ if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 } # Build # -build { core init app/chroot drivers/timer/linux test/timer } +build { core init drivers/timer/linux test/timer } if {[catch { exec which setcap }]} { puts stderr "Error: setcap not available, please install the libcap2-bin package" @@ -42,27 +42,8 @@ set config { - + - - - - - - - - - - - - - - - - - - - } @@ -91,16 +72,16 @@ install_config $config # run directory into the chroot environment. If the directory entries # were symlinks, those would point to nowhere within the chroot. # -foreach binary { core init chroot timer test-timer } { +foreach binary { core init timer test-timer } { exec cp -H bin/$binary [run_dir] } # -# Grant chroot permission to 'chroot' tool +# Grant chroot permission to core # # CAP_SYS_ADMIN is needed for bind mounting genode runtime directories # CAP_SYS_CHROOT is needed to perform the chroot syscall # -exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/chroot +exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/core # # Setup chroot environment @@ -111,8 +92,8 @@ cleanup_chroot exec mkdir -p [chroot_path] exec mkdir -p [chroot_path]/lib -# bind mount '/lib' as need libc within the chroot environment -exec sudo mount --bind /lib [chroot_path]/lib +# bind mount '/lib' as we need ldso and libc within the chroot environment +exec sudo mount --bind /lib [chroot_path]/lib # # Execute test case diff --git a/os/run/chroot_loader.run b/os/run/chroot_loader.run index d4536f4c4..9c120ebd3 100644 --- a/os/run/chroot_loader.run +++ b/os/run/chroot_loader.run @@ -10,7 +10,7 @@ if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 } # Build # -build { core init app/chroot drivers/timer/linux test/timer +build { core init drivers/timer/linux test/timer server/loader test/chroot_loader } if {[catch { exec which setcap }]} { @@ -62,12 +62,9 @@ proc chroot_path { id } { return "/tmp/chroot-test-$id" } proc chroot_cwd_path { id } { return "[chroot_path $id][pwd]/[run_dir]" } -proc chroot_genode_tmp_path { id } { return "[chroot_path $id]/tmp/genode-[exec id -u]" } - proc cleanup_chroot { } { foreach id { 1 2 } { catch { exec sudo umount -l [chroot_cwd_path $id] } - catch { exec sudo umount -l [chroot_genode_tmp_path $id] } catch { exec sudo umount -l [chroot_path $id]/lib } catch { exec sudo umount -l [chroot_path $id]/lib64 } catch { exec rm -rf [chroot_path $id] } @@ -88,16 +85,16 @@ install_config $config # run directory into the chroot environment. If the directory entries # were symlinks, those would point to nowhere within the chroot. # -foreach binary { core init chroot timer loader test-chroot_loader test-timer} { +foreach binary { core init timer loader test-chroot_loader test-timer} { exec cp -H bin/$binary [run_dir] } # -# Grant chroot permission to 'chroot' tool +# Grant chroot permission to 'core' # # CAP_SYS_ADMIN is needed for bind mounting genode runtime directories # CAP_SYS_CHROOT is needed to perform the chroot syscall # -exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/chroot +exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/core # # Setup chroot environment @@ -129,7 +126,7 @@ if {[regexp -all -- {--- timer test ---} $output] != 6} { exit 2 } -if {![regexp -- {chroot-1 -> test-timer] wait 2/10} $output]} { +if {![regexp -- {init-1 -> test-timer] wait 2/10} $output]} { puts stderr "Long-running timer test has made too little progress" exit 3 } diff --git a/os/src/app/chroot/main.cc b/os/src/app/chroot/main.cc deleted file mode 100644 index e8ef01e95..000000000 --- a/os/src/app/chroot/main.cc +++ /dev/null @@ -1,207 +0,0 @@ -/* - * \brief Utility for using the Linux chroot mechanism with Genode - * \author Norman Feske - * \date 2012-04-18 - */ - -/* Genode includes */ -#include -#include - -/* Linux includes */ -#include -#include -#include -#include -#include - - -enum { MAX_PATH_LEN = 256 }; - -static bool verbose = false; - - -/** - * Return true if specified path is an existing directory - */ -static bool is_directory(char const *path) -{ - struct stat s; - if (stat(path, &s) != 0) - return false; - - if (!(s.st_mode & S_IFDIR)) - return false; - - return true; -} - - -static bool is_path_delimiter(char c) { return c == '/'; } - - -static bool has_trailing_path_delimiter(char const *path) -{ - char last_char = 0; - for (; *path; path++) - last_char = *path; - - return is_path_delimiter(last_char); -} - - -/** - * Return number of path elements of given path - */ -static size_t num_path_elements(char const *path) -{ - size_t count = 0; - - /* - * If path starts with non-slash, the first characters belongs to a path - * element. - */ - if (*path && !is_path_delimiter(*path)) - count = 1; - - /* count slashes */ - for (; *path; path++) - if (is_path_delimiter(*path)) - count++; - - return count; -} - - -static bool leading_path_elements(char const *path, unsigned num, - char *dst, size_t dst_len) -{ - /* counter of path delimiters */ - unsigned count = 0; - unsigned i = 0; - - if (is_path_delimiter(path[0])) - num++; - - for (; path[i] && (count < num) && (i < dst_len); i++) - { - if (is_path_delimiter(path[i])) - count++; - - if (count == num) - break; - - dst[i] = path[i]; - } - - if (i + 1 < dst_len) { - dst[i] = 0; - return true; - } - - /* string is cut, append null termination anyway */ - dst[dst_len - 1] = 0; - return false; -} - - -static void mirror_path_to_chroot(char const *chroot_path, char const *path) -{ - char target_path[MAX_PATH_LEN]; - Genode::snprintf(target_path, sizeof(target_path), "%s%s", - chroot_path, path); - - /* - * Create directory hierarchy pointing to the target path except for the - * last element. The last element will be bind-mounted to refer to the - * original 'path'. - */ - for (unsigned i = 1; i <= num_path_elements(target_path); i++) - { - char buf[MAX_PATH_LEN]; - leading_path_elements(target_path, i, buf, sizeof(buf)); - - /* skip existing directories */ - if (is_directory(buf)) - continue; - - /* create new directory */ - mkdir(buf, 0777); - } - - umount(target_path); - - if (verbose) { - PINF("bind mount from: %s", path); - PINF(" to: %s", target_path); - } - - if (mount(path, target_path, 0, MS_BIND, 0)) - PERR("bind mount failed (errno=%d: %s)", errno, strerror(errno)); -} - - -int main(int, char **argv) -{ - using namespace Genode; - - static char chroot_path[MAX_PATH_LEN]; - static char cwd_path[MAX_PATH_LEN]; - - /* - * Read configuration - */ - try { - Xml_node config = Genode::config()->xml_node(); - - config.sub_node("root").attribute("path") - .value(chroot_path, sizeof(chroot_path)); - - verbose = config.attribute("verbose").has_value("yes"); - - } catch (...) { - PERR("invalid config"); - return 1; - } - - getcwd(cwd_path, sizeof(cwd_path)); - - /* - * Print diagnostic information - */ - if (verbose) { - PINF("work directory: %s", cwd_path); - PINF("chroot path: %s", chroot_path); - } - - /* - * Validate chroot path - */ - if (!is_directory(chroot_path)) { - PERR("chroot path does not point to valid directory"); - return 2; - } - - if (has_trailing_path_delimiter(chroot_path)) { - PERR("chroot path has trailing slash"); - return 3; - } - - /* - * Hardlink directories needed for running Genode within the chroot - * environment. - */ - mirror_path_to_chroot(chroot_path, cwd_path); - - printf("changing root to %s ...\n", chroot_path); - - if (chroot(chroot_path)) { - PERR("chroot failed (errno=%d: %s)", errno, strerror(errno)); - return 4; - } - - execve("init", argv, environ); - - PERR("execve failed with errno=%d", errno); - return 0; -} diff --git a/os/src/app/chroot/target.mk b/os/src/app/chroot/target.mk deleted file mode 100644 index 5a49c45b4..000000000 --- a/os/src/app/chroot/target.mk +++ /dev/null @@ -1,4 +0,0 @@ -TARGET = chroot -REQUIRES = linux -SRC_CC = main.cc -LIBS = cxx env server lx_hybrid diff --git a/os/src/server/loader/child.h b/os/src/server/loader/child.h index a3c8db88f..382738bcf 100644 --- a/os/src/server/loader/child.h +++ b/os/src/server/loader/child.h @@ -36,6 +36,11 @@ namespace Loader { Label(char const *l) { strncpy(string, l, sizeof(string)); } } _label; + struct Path { + char string[Session::Path::MAX_SIZE]; + Path(char const *l) { strncpy(string, l, sizeof(string)); } + } _root; + Rpc_entrypoint &_ep; struct Resources @@ -70,8 +75,9 @@ namespace Loader { Rom_session_client _binary_rom_session; - Init::Child_policy_provide_rom_file _binary_policy; - Init::Child_policy_enforce_labeling _labeling_policy; + Init::Child_policy_provide_rom_file _binary_policy; + Init::Child_policy_enforce_labeling _labeling_policy; + Init::Child_policy_prepend_chroot_path _chroot_policy; int _max_width, _max_height; @@ -91,8 +97,9 @@ namespace Loader { public: - Child(const char *binary_name, - const char *label, + Child(char const *binary_name, + char const *label, + char const *root, Rpc_entrypoint &ep, Ram_session_client &ram_session_client, size_t ram_quota, @@ -102,6 +109,7 @@ namespace Loader { int max_width, int max_height) : _label(label), + _root(root), _ep(ep), _resources(_label.string, ram_session_client, ram_quota), _parent_services(parent_services), @@ -110,6 +118,7 @@ namespace Loader { _binary_rom_session(_rom_session(binary_name)), _binary_policy("binary", _binary_rom_session.dataspace(), &_ep), _labeling_policy(_label.string), + _chroot_policy(_root.string), _max_width(max_width), _max_height(max_height), _child(_binary_rom_session.dataspace(), _resources.ram.cap(), _resources.cpu.cap(), @@ -127,10 +136,12 @@ namespace Loader { ****************************/ const char *name() const { return _label.string; } + char const *root() const { return _root.string; } void filter_session_args(char const *service, char *args, size_t args_len) { - _labeling_policy.filter_session_args(0, args, args_len); + _labeling_policy.filter_session_args(service, args, args_len); + _chroot_policy. filter_session_args(service, args, args_len); if (!strcmp(service, "Nitpicker")) { diff --git a/os/src/server/loader/main.cc b/os/src/server/loader/main.cc index 2533d5ca8..6496565e7 100644 --- a/os/src/server/loader/main.cc +++ b/os/src/server/loader/main.cc @@ -244,7 +244,7 @@ namespace Loader { _nitpicker_service.view_ready_sigh = sigh; } - void start(Name const &binary_name, Name const &label) + void start(Name const &binary_name, Name const &label, Path const &root) { if (_child) { PWRN("cannot start subsystem twice"); @@ -257,9 +257,10 @@ namespace Loader { try { _child = new (&_md_alloc) - Child(binary_name.string(), label.string(), _ep, - _ram_session_client, ram_quota, _parent_services, - _rom_service, _nitpicker_service, _width, _height); + Child(binary_name.string(), label.string(), + root.string(), _ep, _ram_session_client, + ram_quota, _parent_services, _rom_service, + _nitpicker_service, _width, _height); } catch (Genode::Parent::Service_denied) { throw Rom_module_does_not_exist(); } diff --git a/os/src/test/chroot_loader/main.cc b/os/src/test/chroot_loader/main.cc index 8b2fce76c..c604e19ba 100644 --- a/os/src/test/chroot_loader/main.cc +++ b/os/src/test/chroot_loader/main.cc @@ -48,14 +48,11 @@ static char const *chroot_path_of_dynamic_test() **********/ /** - * Return format string used as template for the subsystem configuration. - * - * Note the format-string argument used for inserting the chroot path. + * Return subsystem configuration. */ -static char const *config_template() +static char const *subsystem_config() { return "\n" - " \n" " \n" " \n" " \n" @@ -90,7 +87,7 @@ class Chroot_subsystem /** * Import data as ROM module into the subsystem-specific ROM service */ - void _import_rom_module(char const *name, void *ptr, Genode::size_t size) + void _import_rom_module(char const *name, void const *ptr, Genode::size_t size) { using namespace Genode; @@ -113,20 +110,17 @@ class Chroot_subsystem using namespace Genode; /* - * Generate Genode configuration of the new subsystem and import - * it into the subsystem's loader session as a ROM module named - * "config". + * Import subsystem's configuration into the subsystem's loader + * session as a ROM module named "config". */ - char buf[strlen(chroot_path) + strlen(config_template()) + 1]; - snprintf(buf, sizeof(buf), config_template(), chroot_path); - - _import_rom_module("config", buf, strlen(buf) + 1); + _import_rom_module("config", subsystem_config(), + strlen(subsystem_config()) + 1); /* * Name of the Genode binary is start as the root of the new * subsystem. */ - char const *chroot_binary_name = "chroot"; + char const *binary_name = "init"; /* * Generate unique label name using a counter @@ -136,10 +130,12 @@ class Chroot_subsystem * for validating the test in the run script. */ static int cnt = 0; - snprintf(_label, sizeof(_label), "%s-%d", chroot_binary_name, ++cnt); + snprintf(_label, sizeof(_label), "%s-%d", binary_name, ++cnt); /* start execution of new subsystem */ - _loader.start(chroot_binary_name, Loader::Session::Name(_label)); + _loader.start(binary_name, + Loader::Session::Name(_label), + Loader::Session::Path(chroot_path)); } };