diff --git a/run/chroot.run b/run/chroot.run new file mode 100644 index 0000000..cf56009 --- /dev/null +++ b/run/chroot.run @@ -0,0 +1,129 @@ +# +# \brief Chroot test +# \author Emery Hemingway +# \date 2016-12-03 +# + +# +# Build +# + +build { core init server/vfs server/chroot test/libc } + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +build_boot_image { + core init vfs chroot + ld.lib.so libc.lib.so libm.lib.so + test-libc +} + +# +# Execute test case +# + +append qemu_args " -nographic -m 64 " +run_genode_until {child "test-libc-3" exited with exit value 0} 60 + +# vi: set ft=tcl : diff --git a/src/server/chroot/component.cc b/src/server/chroot/component.cc index d64e826..5d6f3d5 100644 --- a/src/server/chroot/component.cc +++ b/src/server/chroot/component.cc @@ -16,100 +16,132 @@ #include #include #include -#include #include #include -#include +#include #include #include #include -using namespace Genode; +namespace Chroot { + using namespace Genode; + struct Main; + template + static void path_from_label(Path &path, char const *label) + { + char tmp[MAX_LEN]; + size_t len = strlen(label); -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("/"); - 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); - 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; + 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); } - 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> + +struct Chroot::Main { - Genode::Attached_rom_dataspace _config_rom; - Parent_service _parent_service; - Heap _heap; - Allocator_avl _fs_tx_block_alloc { &_heap }; - File_system::Connection _fs { _fs_tx_block_alloc, 1024 }; + enum { PATH_MAX_LEN = 128 }; + typedef Genode::Path Path; + + struct Session : Parent::Server + { + Parent::Client parent_client; + + Id_space::Element client_id; + Id_space::Element server_id; + + Session(Id_space &client_space, + Id_space &server_space, + Parent::Server::Id server_id) + : + client_id(parent_client, client_space), + server_id(*this, server_space, server_id) { } + }; + + Genode::Env &env; + + Id_space server_id_space; + + Heap heap { env.ram(), env.rm() }; + + Allocator_avl fs_tx_block_alloc { &heap }; + File_system::Connection fs { fs_tx_block_alloc, 1 }; + + Attached_rom_dataspace session_requests { env, "session_requests" }; + + Attached_rom_dataspace config_rom { env, "config" }; + + void handle_session_request(Xml_node request); + + void handle_session_requests() + { + if (config_sig_rec.pending()) { + do { config_sig_rec.pending_signal(); } + while (config_sig_rec.pending()); + config_rom.update(); + } + + session_requests.update(); + + Xml_node const requests = session_requests.xml(); + + requests.for_each_sub_node([&] (Xml_node request) { + handle_session_request(request); + }); + } + + Signal_handler
session_request_handler { + env.ep(), *this, &Main::handle_session_requests }; + + Signal_context config_sig_ctx; + Signal_receiver config_sig_rec; /** * Constructor */ - Proxy(Genode::Env &env) - : - _config_rom(env, "config"), - _parent_service("File_system"), - _heap(env.ram(), env.rm()) + Main(Genode::Env &env) : env(env) { - env.parent().announce(env.ep().rpc_ep().manage(this)); + config_rom.sigh(config_sig_rec.manage(&config_sig_ctx)); + session_requests.sigh(session_request_handler); } - Session_capability chroot(char const *args, char const *path, Affinity const &affinity) + ~Main() { + config_sig_rec.dissolve(&config_sig_ctx); } + + Session_capability request_session(Parent::Client::Id const &id, + Session_state::Args const &args) { - enum { ARGS_MAX_LEN = 256 }; - char new_args[ARGS_MAX_LEN]; + char tmp[PATH_MAX_LEN]; + Path root_path; - 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 = label_from_args(session_args.string()); + Session_label label = label_from_args(args.string()); char const *label_str = label.string(); + /* if policy specifies a merge, use a truncated label */ try { - Session_policy policy(label, _config_rom.xml()); + Session_policy policy(label, config_rom.xml()); if (policy.has_attribute("label_prefix") && policy.attribute_value("merge", false)) @@ -127,48 +159,111 @@ struct Proxy : Rpc_object> } } catch (Session_policy::No_policy_defined) { } + + /* create a new path from the label */ path_from_label(root_path, label_str); - Arg_string::find_arg(session_args.string(), "root").string( + /* extract and append the orginal root */ + Arg_string::find_arg(args.string(), "root").string( tmp, sizeof(tmp), "/"); root_path.append_element(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; + + /* create the new root directory if it is missing */ + try { fs.close(ensure_dir(fs, new_root)); } + catch (Node_already_exists) { } + catch (Permission_denied) { + Genode::error(new_root,": permission denied"); throw; } + catch (Name_too_long) { + Genode::error(new_root,": new root too long"); throw; } + catch (No_space) { + Genode::error(new_root,": no space"); throw; } + catch (...) { + Genode::error(new_root,": unknown error"); throw; } + + /* rewrite the root session argument */ + enum { ARGS_MAX_LEN = 256 }; + char new_args[ARGS_MAX_LEN]; + + strncpy(new_args, args.string(), ARGS_MAX_LEN); + Arg_string::set_arg_string(new_args, ARGS_MAX_LEN, "root", new_root); + + Affinity affinity; + return env.session("File_system", id, new_args, affinity); + } +}; + + +void Chroot::Main::handle_session_request(Xml_node request) +{ + if (!request.has_attribute("id")) + return; + + Parent::Server::Id const server_id { request.attribute_value("id", 0UL) }; + + if (request.has_type("create")) { + + if (!request.has_sub_node("args")) + return; + + typedef Session_state::Args Args; + Args const args = request.sub_node("args").decoded_content(); + + Session *session = nullptr; try { - _fs.close(ensure_dir(_fs, new_root)); - return chroot(args, new_root, affinity); + session = new (heap) + Session(env.id_space(), server_id_space, server_id); + + Session_capability cap = request_session(session->client_id.id(), args); + + env.parent().deliver_session_cap(server_id, cap); } - 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"; } - - Genode::error(new_root, ": ", errstr); - throw Root::Unavailable(); + catch (...) { + if (session) + destroy(heap, session); + env.parent().session_response(server_id, Parent::INVALID_ARGS); + } } - void upgrade(Session_capability cap, - Root::Upgrade_args const &args) override { - _parent_service.upgrade(cap, args.string()); } + if (request.has_type("upgrade")) { - void close(Session_capability cap) override { - _parent_service.close(cap); } -}; + server_id_space.apply(server_id, [&] (Session &session) { + + size_t ram_quota = request.attribute_value("ram_quota", 0UL); + + char buf[64]; + snprintf(buf, sizeof(buf), "ram_quota=%ld", ram_quota); + + // XXX handle Root::Invalid_args + env.upgrade(session.client_id.id(), buf); + env.parent().session_response(server_id, Parent::SESSION_OK); + }); + } + + if (request.has_type("close")) { + server_id_space.apply(server_id, [&] (Session &session) { + env.close(session.client_id.id()); + destroy(heap, &session); + env.parent().session_response(server_id, Parent::SESSION_CLOSED); + }); + } +} /*************** ** Component ** ***************/ -Genode::size_t Component::stack_size() { return 2*1024*sizeof(Genode::addr_t); } +Genode::size_t Component::stack_size() { + return 2*1024*sizeof(Genode::addr_t); } -void Component::construct(Genode::Env &env) { static Proxy inst(env); } +void Component::construct(Genode::Env &env) +{ + static Chroot::Main inst(env); + env.parent().announce("File_system"); +}