server/chroot: change root server

Fixes #11
This commit is contained in:
Emery Hemingway
2016-03-11 11:28:23 +01:00
committed by Norman Feske
parent 58b58f1008
commit 08668bfd1a
3 changed files with 212 additions and 0 deletions

33
src/server/chroot/README Normal file
View File

@@ -0,0 +1,33 @@
This component intercepts File_system requests and changes
the root directory of the request using the session label.
In this example if cli_monitor had a child named "X", every
file system session from "X" would be rooted to the directory
"/cli_monitor/X" at "fs_server".
! <start name="fs_server">
! <provides> <service name="File_system"/> </provides>
! ...
! </start>
!
! <start name="chroot">
! <provides> <service name="File_system"/> </provides>
! <config>
! <policy label_prefix="cli_monitor ->" merge="yes"/>
! </config>
! <route>
! <any-service>
! <child name="fs_server"/> <parent/>
! </any-service>
! </route>
! ...
! </start>
!
! <start name="cli_monitior">
! <route>
! <any-service>
! <child name="chroot"/> <parent/>
! </any-service>
! </route>
! ...
! </start>

176
src/server/chroot/main.cc Normal file
View File

@@ -0,0 +1,176 @@
/*
* \brief Change session root server
* \author Emery Hemingway
* \date 2016-03-10
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <file_system/util.h>
#include <file_system_session/connection.h>
#include <cap_session/connection.h>
#include <os/path.h>
#include <os/session_policy.h>
#include <root/root.h>
#include <base/rpc_server.h>
#include <base/service.h>
#include <base/allocator_avl.h>
#include <base/sleep.h>
using namespace Genode;
template <unsigned MAX_LEN>
static void path_from_label(Path<MAX_LEN> &path, char const *label)
{
char tmp[MAX_LEN];
size_t len = strlen(label);
size_t i = 0;
for (size_t j = 1; j < len; ++j) {
if (!strcmp(" -> ", label+j, 4)) {
path.append("/");
strncpy(tmp, label+i, (j-i)+1);
/* rewrite any directory seperators */
for (size_t k = 0; k < MAX_LEN; ++k)
if (tmp[k] == '/')
tmp[k] = '_';
path.append(tmp);
j += 4;
i = j;
}
}
path.append("/");
strncpy(tmp, label+i, MAX_LEN);
/* rewrite any directory seperators */
for (size_t k = 0; k < MAX_LEN; ++k)
if (tmp[k] == '/')
tmp[k] = '_';
path.append(tmp);
}
struct Proxy : Rpc_object<Typed_root<File_system::Session>>
{
Parent_service _parent_service;
Allocator_avl _fs_tx_block_alloc;
File_system::Connection _fs;
/**
* Constructor
*/
Proxy()
:
_parent_service("File_system"),
_fs_tx_block_alloc(env()->heap()),
_fs(_fs_tx_block_alloc, 1024)
{ }
Session_capability chroot(char const *args, char const *path, Affinity const &affinity)
{
enum { ARGS_MAX_LEN = 256 };
char new_args[ARGS_MAX_LEN];
strncpy(new_args, args, ARGS_MAX_LEN);
Arg_string::set_arg_string(new_args, ARGS_MAX_LEN, "root", path);
try { return _parent_service.session(new_args, affinity); }
catch (Service::Invalid_args) { throw Root::Invalid_args(); }
catch (Service::Quota_exceeded) { throw Root::Quota_exceeded(); }
catch (...) { }
throw Root::Unavailable();
}
/********************
** Root interface **
********************/
Session_capability session(Root::Session_args const &session_args,
Affinity const &affinity)
{
enum { MAX_LEN = 128 };
char tmp[MAX_LEN];
Path<MAX_LEN> root_path;
Session_label label(session_args.string());
char const *label_str = label.string();
try {
Session_policy policy(label);
if (policy.has_attribute("label_prefix")
&& policy.attribute_value("merge", false))
{
/* merge at the next element */
size_t offset = policy.attribute("label_prefix").value_size();
for (size_t i = offset; i < label.length()-4; ++i) {
if (strcmp(label_str+i, " -> ", 4))
continue;
strncpy(tmp, label_str, min(sizeof(tmp), i+1));
label_str = tmp;
break;
}
}
} catch (Session_policy::No_policy_defined) { }
path_from_label(root_path, label_str);
Arg_string::find_arg(session_args.string(), "root").string(
tmp, sizeof(tmp), "/");
root_path.append(tmp);
root_path.remove_trailing('/');
char const *args = session_args.string();
char const *new_root = root_path.base();
using namespace File_system;
Dir_handle handle;
char const *errstr;
try {
_fs.close(ensure_dir(_fs, new_root));
return chroot(args, new_root, affinity);
}
catch (Node_already_exists) { return chroot(args, new_root, affinity); }
catch (Permission_denied) { errstr = "permission denied"; }
catch (Name_too_long) { errstr = "new root too long"; }
catch (No_space) { errstr = "no space"; }
catch (...) { errstr = "unknown error"; }
PERR("%s: %s", new_root, errstr);
throw Root::Unavailable();
}
void upgrade(Session_capability cap,
Root::Upgrade_args const &args) override {
_parent_service.upgrade(cap, args.string()); }
void close(Session_capability cap) override {
_parent_service.close(cap); }
};
int main(void)
{
static Cap_connection cap;
static Proxy proxy;
enum { STACK_SIZE = 4096 * 4 };
static Rpc_entrypoint ep(&cap, STACK_SIZE, "chroot_ep");
env()->parent()->announce(ep.manage(&proxy));
/* let the entrypoint take over */
sleep_forever();
}

View File

@@ -0,0 +1,3 @@
TARGET = chroot
SRC_CC = main.cc
LIBS = base config