Add chroot support to core

Since the recent move of the process creation into core, the original chroot trampoline
mechanism implemented in 'os/src/app/chroot' does not work anymore. A
process could simply escape the chroot environment by spawning a new
process via core's PD service. Therefore, this patch moves the chroot
support into core. So the chroot policy becomes mandatory part of the
process creation.  For each process created by core, core checks for
'root' argument of the PD session. If a path is present, core takes the
precautions needed to execute the new process in the specified chroot
environment.

This conceptual change implies minor changes with respect to the Genode
API and the configuration of the init process. The API changes are the
enhancement of the 'Genode::Child' and 'Genode::Process' constructors to
take the root path as argument. Init supports the specification of a
chroot per process by specifying the new 'root' attribute to the
'<start>' node of the process. In line with these changes, the
'Loader::Session::start' function has been enhanced with the additional
(optional) root argument.
This commit is contained in:
Norman Feske 2012-10-24 16:27:26 +02:00
parent e1e7ab178f
commit b45242c50f
22 changed files with 401 additions and 295 deletions

View File

@ -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())
{

View File

@ -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 **
********************************************/

View File

@ -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;

View File

@ -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),
"<unlabeled>");
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<Dataspace> 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);

View File

@ -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 ""; }
};

View File

@ -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

View File

@ -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<Pd_session>(session("ram_quota=4K, label=\"%s\"", label)),
Connection<Pd_session>(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())
{ }
};

View File

@ -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())
{ }

View File

@ -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)
{

View File

@ -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,

View File

@ -262,6 +262,29 @@ as LOG output. To enable the verbose mode, assign the value "yes" to the
'verbose' attribute of the '<config>' 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 '<start>' 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
###############################

View File

@ -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; }
};
}

View File

@ -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; }

View File

@ -41,8 +41,8 @@ namespace Loader {
void view_ready_sigh(Signal_context_capability sigh) {
call<Rpc_view_ready_sigh>(sigh); }
void start(Name const &binary, Name const &label = "") {
call<Rpc_start>(binary, label); }
void start(Name const &binary, Name const &label = "", Path const &root = "") {
call<Rpc_start>(binary, label, root); }
Nitpicker::View_capability view() {
return call<Rpc_view>(); }

View File

@ -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);

View File

@ -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 {
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="chroot">
<start name="test-timer" root="chroot_path">
<resource name="RAM" quantum="1G"/>
<config verbose="yes">
<root path="chroot_path" />
<parent-provides>
<service name="ROM"/>
<service name="LOG"/>
<service name="CAP"/>
<service name="RAM"/>
<service name="CPU"/>
<service name="RM"/>
<service name="PD"/>
<service name="Timer"/>
</parent-provides>
<default-route>
<any-service> <parent/> </any-service>
</default-route>
<start name="test-timer">
<resource name="RAM" quantum="1G"/>
</start>
</config>
</start>
</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

View File

@ -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
}

View File

@ -1,207 +0,0 @@
/*
* \brief Utility for using the Linux chroot mechanism with Genode
* \author Norman Feske
* \date 2012-04-18
*/
/* Genode includes */
#include <os/config.h>
#include <base/sleep.h>
/* Linux includes */
#include <sys/stat.h>
#include <sys/mount.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
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;
}

View File

@ -1,4 +0,0 @@
TARGET = chroot
REQUIRES = linux
SRC_CC = main.cc
LIBS = cxx env server lx_hybrid

View File

@ -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")) {

View File

@ -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(); }

View File

@ -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 "<config verbose=\"yes\">\n"
" <root path=\"%s\" />\n"
" <parent-provides>\n"
" <service name=\"ROM\"/>\n"
" <service name=\"LOG\"/>\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));
}
};