mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-09 12:02:57 +00:00
Mechanism for using chroot on Linux
The new 'chroot' tool at 'os/src/app/chroot' allows for executing subsystems within chroot jails on Linux. For using the tool, please refer to the test case 'os/run/chroot.run'. Fixes #37
This commit is contained in:
parent
48d547e2fd
commit
88aab61e09
122
os/run/chroot.run
Normal file
122
os/run/chroot.run
Normal file
@ -0,0 +1,122 @@
|
||||
#
|
||||
# \brief Test for using chroot on Linux
|
||||
# \author Norman Feske
|
||||
# \date 2012-04-18
|
||||
#
|
||||
#
|
||||
if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 }
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
build { core init app/chroot drivers/timer/linux test/timer }
|
||||
|
||||
if {[catch { exec which setcap }]} {
|
||||
puts stderr "Error: setcap not available, please install the libcap2-bin package"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
set config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="CAP"/>
|
||||
<service name="RAM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="RM"/>
|
||||
<service name="PD"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="chroot">
|
||||
<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>
|
||||
}
|
||||
|
||||
proc chroot_path { } { return "/tmp/chroot-test" }
|
||||
proc chroot_cwd_path { } { return "[chroot_path][pwd]/[run_dir]" }
|
||||
proc chroot_genode_tmp_path { } { return "[chroot_path]/tmp/genode-[exec id -u]" }
|
||||
|
||||
proc cleanup_chroot { } {
|
||||
catch { exec sudo umount -l [chroot_cwd_path] }
|
||||
catch { exec sudo umount -l [chroot_genode_tmp_path] }
|
||||
exec rm -rf [chroot_path]
|
||||
}
|
||||
|
||||
# replace 'chroot_path' marker in config with actual path
|
||||
regsub "chroot_path" $config [chroot_path] config
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Copy boot modules into run directory
|
||||
#
|
||||
# We cannot use the predefined 'build_boot_image' function here because
|
||||
# this would create mere symlinks. However, we want to hardlink the
|
||||
# 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 } {
|
||||
exec cp -H bin/$binary [run_dir] }
|
||||
|
||||
#
|
||||
# Grant chroot permission to 'chroot' tool
|
||||
#
|
||||
# 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
|
||||
|
||||
#
|
||||
# Setup chroot environment
|
||||
#
|
||||
|
||||
# start with fresh directory
|
||||
cleanup_chroot
|
||||
exec mkdir -p [chroot_path]
|
||||
|
||||
#
|
||||
# Execute test case
|
||||
#
|
||||
run_genode_until {.*--- timer test finished ---.*} 60
|
||||
|
||||
#
|
||||
# Remove artifacts created while running the test
|
||||
#
|
||||
cleanup_chroot
|
||||
|
||||
# vi: set ft=tcl :
|
211
os/src/app/chroot/main.cc
Normal file
211
os/src/app/chroot/main.cc
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* \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];
|
||||
static char genode_tmp_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));
|
||||
|
||||
uid_t const uid = getuid();
|
||||
snprintf(genode_tmp_path, sizeof(genode_tmp_path), "/tmp/genode-%d", uid);
|
||||
|
||||
/*
|
||||
* Print diagnostic information
|
||||
*/
|
||||
if (verbose) {
|
||||
PINF("work directory: %s", cwd_path);
|
||||
PINF("chroot path: %s", chroot_path);
|
||||
PINF("genode tmp path: %s", genode_tmp_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);
|
||||
mirror_path_to_chroot(chroot_path, genode_tmp_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);
|
||||
return 0;
|
||||
}
|
19
os/src/app/chroot/target.mk
Normal file
19
os/src/app/chroot/target.mk
Normal file
@ -0,0 +1,19 @@
|
||||
TARGET = chroot
|
||||
REQUIRES = linux
|
||||
SRC_CC = main.cc
|
||||
LIBS = cxx env server lx_hybrid
|
||||
|
||||
#
|
||||
# XXX find a way to remove superfluous warning:
|
||||
#
|
||||
# base/include/util/token.h: In constructor ‘Genode::Config::Config()’:
|
||||
# base/include/util/token.h:69:67: warning: ‘ret’ may be used uninitialized in
|
||||
# this function [-Wuninitialized]
|
||||
# base/include/base/capability.h:196:62: note: ‘ret’ was declared here
|
||||
# base/include/util/token.h:100:68: warning: ‘prephitmp.1897’ may be used
|
||||
# uninitialized in this function
|
||||
# [-Wuninitialized]
|
||||
# os/include/os/config.h:42:4: note: ‘prephitmp.1897’ was declared here
|
||||
#
|
||||
CC_WARN = -Wall -Wno-uninitialized
|
||||
|
Loading…
x
Reference in New Issue
Block a user