212 lines
4.1 KiB
C++
212 lines
4.1 KiB
C++
|
/*
|
||
|
* \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;
|
||
|
}
|