diff --git a/src/server/chroot/README b/src/server/chroot/README new file mode 100644 index 0000000..50a899b --- /dev/null +++ b/src/server/chroot/README @@ -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". + +! +! +! ... +! +! +! +! +! +! +! +! +! +! +! +! +! ... +! +! +! +! +! +! +! +! +! ... +! \ No newline at end of file diff --git a/src/server/chroot/main.cc b/src/server/chroot/main.cc new file mode 100644 index 0000000..b7613e1 --- /dev/null +++ b/src/server/chroot/main.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Genode; + + +template +static void path_from_label(Path &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> +{ + 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 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(); +} diff --git a/src/server/chroot/target.mk b/src/server/chroot/target.mk new file mode 100644 index 0000000..5e423da --- /dev/null +++ b/src/server/chroot/target.mk @@ -0,0 +1,3 @@ +TARGET = chroot +SRC_CC = main.cc +LIBS = base config \ No newline at end of file