2
0
Fork 0
genodepkgs/packages/genodelabs/cached_fs_rom.patch

737 lines
22 KiB
Diff
Raw Normal View History

From 3ff27b172afd3ee436f9cce754c15d5913f0cb86 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Tue, 3 Nov 2020 15:28:56 +0100
Subject: [PATCH 1/2] cached_fs_rom: resolve symlinks
Follow symlinks from ROM requests to files. This complicates the
implementation but allows ROM requests to be redirected at both the
label routing and the file-system layers. Redirecting ROMs with symlinks
is useful for retrieving ROMs from deeply nested or otherwise
excessively long file-system paths.
---
repos/os/src/server/cached_fs_rom/main.cc | 469 +++++++++++++---------
1 file changed, 278 insertions(+), 191 deletions(-)
diff --git a/repos/os/src/server/cached_fs_rom/main.cc b/repos/os/src/server/cached_fs_rom/main.cc
index 9e4e4d0eee..5f2aea63f5 100755
--- a/repos/os/src/server/cached_fs_rom/main.cc
+++ b/repos/os/src/server/cached_fs_rom/main.cc
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2018 Genode Labs GmbH
+ * Copyright (C) 2018-2020 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
@@ -26,32 +26,119 @@
/* local session-requests utility */
#include "session_requests.h"
-/*****************
- ** ROM service **
- *****************/
namespace Cached_fs_rom {
using namespace Genode;
- typedef Genode::Path<File_system::MAX_PATH_LEN> Path;
- typedef File_system::Session_client::Tx::Source Tx_source;
-
- struct Cached_rom;
- typedef Genode::Id_space<Cached_rom> Cache_space;
struct Transfer;
typedef Genode::Id_space<Transfer> Transfer_space;
+ struct Cached_rom;
+ typedef Genode::Id_space<Cached_rom> Cache_space;
+
class Session_component;
typedef Genode::Id_space<Session_component> Session_space;
struct Main;
+ typedef Genode::Path<File_system::MAX_PATH_LEN> Path;
+ typedef File_system::Session_client::Tx::Source Tx_source;
typedef File_system::Session::Tx::Source::Packet_alloc_failed Packet_alloc_failed;
- typedef File_system::File_handle File_handle;
}
+/**
+ * State of symlink resolution and file reading
+ */
+struct Cached_fs_rom::Transfer final
+{
+ Transfer_space &_transfers;
+ File_system::Session &_fs;
+
+ Path const first_path;
+ Path final_path = first_path;
+
+ Reconstructible<Transfer_space::Element> _transfer_elem {
+ *this, _transfers,
+ Transfer_space::Id{_fs.node(first_path.string()).value}
+ };
+
+ File_system::Packet_descriptor _raw_pkt = _alloc_packet();
+ File_system::Packet_guard _packet_guard { *_fs.tx(), _raw_pkt };
+
+ Attached_ram_dataspace ram_ds;
+ File_system::Status status { };
+
+ /* Id of the originating session request */
+ Parent::Server::Id const request_id;
+
+ File_system::seek_off_t seek = 0;
+
+ bool diag;
+ bool completed = false;
+
+ /**
+ * Allocate space in the File_system packet buffer
+ *
+ * \throw Packet_alloc_failed
+ */
+ File_system::Packet_descriptor _alloc_packet()
+ {
+ if (!_fs.tx()->ready_to_submit())
+ throw Packet_alloc_failed();
+
+ size_t chunk_size = max(
+ size_t(File_system::MAX_PATH_LEN),
+ _fs.tx()->bulk_buffer_size()/4);
+ return _fs.tx()->alloc_packet(chunk_size);
+ }
+
+ File_system::Node_handle handle() const {
+ return File_system::Node_handle{ _transfer_elem->id().value }; }
+
+ void replace_handle(File_system::Node_handle new_handle)
+ {
+ _fs.close(handle());
+ _transfer_elem.destruct();
+ _transfer_elem.construct(
+ *this, _transfers, Transfer_space::Id{new_handle.value});
+ }
+
+ Transfer(Genode::Env &env,
+ File_system::Session &fs,
+ Transfer_space &space,
+ Path path,
+ Parent::Server::Id &pid,
+ bool diag)
+ : _transfers(space)
+ , _fs(fs)
+ , first_path(path)
+ , ram_ds(env.ram(), env.rm(), 0)
+ , request_id(pid)
+ , diag(diag)
+ { }
+
+ ~Transfer() { _fs.close(handle()); }
+
+ bool matches(Path const &path) const {
+ return (first_path == path || final_path == path); }
+
+ void submit_next_packet()
+ {
+ File_system::Packet_descriptor packet(
+ _raw_pkt, handle(),
+ File_system::Packet_descriptor::READ,
+ _raw_pkt.size(), seek);
+ _fs.tx()->submit_packet(packet);
+ }
+
+};
+
+
+/**
+ * A dataspace corresponding to a file-system path
+ */
struct Cached_fs_rom::Cached_rom final
{
Cached_rom(Cached_rom const &);
@@ -60,20 +147,15 @@ struct Cached_fs_rom::Cached_rom final
Genode::Env &env;
Rm_connection &rm_connection;
- size_t const file_size;
-
/**
* Backing RAM dataspace
- *
- * This shall be valid even if the file is empty.
*/
- Attached_ram_dataspace ram_ds {
- env.pd(), env.rm(), file_size ? file_size : 1 };
+ Attached_ram_dataspace ram_ds { env.pd(), env.rm(), 0 };
/**
* Read-only region map exposed as ROM module to the client
*/
- Region_map_client rm { rm_connection.create(ram_ds.size()) };
+ Region_map_client rm_client;
Region_map::Local_addr rm_attachment { };
Dataspace_capability rm_ds { };
@@ -81,48 +163,39 @@ struct Cached_fs_rom::Cached_rom final
Cache_space::Element cache_elem;
- Transfer *transfer = nullptr;
-
/**
* Reference count of cache entry
*/
int _ref_count = 0;
- Cached_rom(Cache_space &cache_space,
- Env &env,
- Rm_connection &rm,
- Path const &file_path,
- size_t size)
- :
- env(env), rm_connection(rm), file_size(size),
- path(file_path),
- cache_elem(*this, cache_space)
+ Cached_rom(Genode::Env &env,
+ Rm_connection &rm,
+ Cache_space &cache_space,
+ Transfer &transfer)
+ : env(env)
+ , rm_connection(rm)
+ , rm_client(rm.create(transfer.ram_ds.size()))
+ , path(transfer.final_path)
+ , cache_elem(*this, cache_space)
{
- if (size == 0)
- complete();
+ /* move dataspace from the transfer object */
+ transfer.ram_ds.swap(ram_ds);
+
+ /* attach dataspace read-only into region map */
+ enum { OFFSET = 0, LOCAL_ADDR = false, EXEC = true, WRITE = false };
+ rm_attachment = rm_client.attach(
+ ram_ds.cap(), ram_ds.size(), OFFSET,
+ LOCAL_ADDR, (addr_t)~0, EXEC, WRITE);
+ rm_ds = rm_client.dataspace();
}
- /**
- * Destructor
- */
~Cached_rom()
{
if (rm_attachment)
- rm.detach(rm_attachment);
+ rm_client.detach(rm_attachment);
}
- bool completed() const { return rm_ds.valid(); }
- bool unused() const { return (_ref_count < 1); }
-
- void complete()
- {
- /* attach dataspace read-only into region map */
- enum { OFFSET = 0, LOCAL_ADDR = false, EXEC = true, WRITE = false };
- rm_attachment = rm.attach(
- ram_ds.cap(), ram_ds.size(), OFFSET,
- LOCAL_ADDR, (addr_t)~0, EXEC, WRITE);
- rm_ds = rm.dataspace();
- }
+ bool unused() const { return (_ref_count < 1); }
/**
* Return dataspace with content of file
@@ -130,6 +203,9 @@ struct Cached_fs_rom::Cached_rom final
Rom_dataspace_capability dataspace() const {
return static_cap_cast<Rom_dataspace>(rm_ds); }
+ /**
+ * Guard for maintaining reference count
+ */
struct Guard
{
Cached_rom &_rom;
@@ -142,97 +218,6 @@ struct Cached_fs_rom::Cached_rom final
};
-struct Cached_fs_rom::Transfer final
-{
- Cached_rom &_cached_rom;
- Cached_rom::Guard _cache_guard { _cached_rom };
-
- File_system::Session &_fs;
- File_system::File_handle _handle;
-
- File_system::file_size_t const _size;
- File_system::seek_off_t _seek = 0;
- File_system::Packet_descriptor _raw_pkt = _alloc_packet();
- File_system::Packet_guard _packet_guard { *_fs.tx(), _raw_pkt };
-
- Transfer_space::Element _transfer_elem;
-
- /**
- * Allocate space in the File_system packet buffer
- *
- * \throw Packet_alloc_failed
- */
- File_system::Packet_descriptor _alloc_packet()
- {
- if (!_fs.tx()->ready_to_submit())
- throw Packet_alloc_failed();
-
- size_t chunk_size = min(_size, _fs.tx()->bulk_buffer_size()/4);
- return _fs.tx()->alloc_packet(chunk_size);
- }
-
- void _submit_next_packet()
- {
- using namespace File_system;
-
- File_system::Packet_descriptor
- packet(_raw_pkt, _handle,
- File_system::Packet_descriptor::READ,
- _raw_pkt.size(), _seek);
-
- _fs.tx()->submit_packet(packet);
- }
-
- public:
-
- /**
- * Constructor
- */
- Transfer(Transfer_space &space,
- Cached_rom &rom,
- File_system::Session &fs,
- File_system::File_handle file_handle,
- size_t file_size)
- :
- _cached_rom(rom), _fs(fs),
- _handle(file_handle), _size(file_size),
- _transfer_elem(*this, space, Transfer_space::Id{_handle.value})
- {
- _cached_rom.transfer = this;
-
- _submit_next_packet();
- }
-
- Path const &path() const { return _cached_rom.path; }
-
- bool completed() const { return (_seek >= _size); }
-
- /**
- * Called from the packet signal handler.
- */
- void process_packet(File_system::Packet_descriptor const packet)
- {
- auto const pkt_seek = packet.position();
-
- if (pkt_seek > _seek || _seek >= _size) {
- error("bad packet seek position for ", path());
- error("packet seek is ", packet.position(), ", file seek is ", _seek, ", file size is ", _size);
- _seek = _size;
- } else {
- size_t const n = min(packet.length(), _size - pkt_seek);
- memcpy(_cached_rom.ram_ds.local_addr<char>()+pkt_seek,
- _fs.tx()->packet_content(packet), n);
- _seek = pkt_seek+n;
- }
-
- if (completed())
- _cached_rom.complete();
- else
- _submit_next_packet();
- }
-};
-
-
class Cached_fs_rom::Session_component final : public Rpc_object<Rom_session>
{
private:
@@ -240,17 +225,15 @@ class Cached_fs_rom::Session_component final : public Rpc_object<Rom_session>
Cached_rom &_cached_rom;
Cached_rom::Guard _cache_guard { _cached_rom };
Session_space::Element _sessions_elem;
- Session_label const _label;
public:
Session_component(Cached_rom &cached_rom,
- Session_space &space, Session_space::Id id,
- Session_label const &label)
+ Session_space &space,
+ Parent::Server::Id pid)
:
_cached_rom(cached_rom),
- _sessions_elem(*this, space, id),
- _label(label)
+ _sessions_elem(*this, space, Session_space::Id{pid.value})
{ }
@@ -273,8 +256,8 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
Rm_connection rm { env };
- Cache_space cache { };
Transfer_space transfers { };
+ Cache_space cache { };
Session_space sessions { };
Heap heap { env.pd(), env.rm() };
@@ -302,36 +285,133 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
return (bool)discard;
}
- /**
- * Open a file handle
- */
- File_system::File_handle open(Path const &file_path)
+ void resolve(Transfer &transfer)
{
using namespace File_system;
+ transfer.status = fs.status(transfer.handle());
- Path dir_path(file_path);
- dir_path.strip_last_element();
- Path file_name(file_path);
- file_name.keep_only_last_element();
+ if (transfer.status.directory()) {
+ error("cannot serve directory as ROM, \"", transfer.final_path, "\"");
+ throw Service_denied();
+ }
+ Path dir_path(transfer.final_path);
+ dir_path.strip_last_element();
Dir_handle parent_handle = fs.dir(dir_path.base(), false);
Handle_guard parent_guard(fs, parent_handle);
- return fs.file(
- parent_handle, file_name.base() + 1,
- File_system::READ_ONLY, false);
+ if (transfer.status.symlink()) {
+ Symlink_handle link_handle = fs.symlink(
+ parent_handle, transfer.final_path.last_element(), false);
+ transfer.replace_handle(link_handle);
+ } else {
+ File_handle file_handle = fs.file(
+ parent_handle, transfer.final_path.last_element(), READ_ONLY, false);
+ transfer.replace_handle(file_handle);
+
+ /* allocate a backing dataspace */
+ size_t ds_size =
+ align_addr(max(transfer.status.size, 1U), 12);
+
+ while (env.pd().avail_ram().value < ds_size
+ || env.pd().avail_caps().value < 8) {
+ /* drop unused cache entries */
+ if (!cache_evict()) break;
+ }
+
+ transfer.ram_ds.realloc(&env.ram(), ds_size);
+ }
+
+ transfer.submit_next_packet();
}
/**
- * Open a file with some exception management
+ * Called from the packet signal handler.
*/
- File_system::File_handle try_open(Path const &file_path)
+ void process_packet(Transfer &transfer,
+ File_system::Packet_descriptor const packet)
+ {
+ if (transfer.status.symlink()) {
+ size_t const n = packet.length();
+ if (n >= Path::capacity()) {
+ error("bad symlink read at ", transfer.final_path);
+ throw Service_denied();
+ }
+
+ char buf[Path::capacity()];
+ copy_cstring(buf, fs.tx()->packet_content(packet), n);
+ if (*buf == '/')
+ transfer.final_path = Path(buf);
+ else
+ transfer.final_path.append_element(buf);
+
+ if (transfer.diag)
+ log("following \"", transfer.first_path, "\" to \"", transfer.final_path, "\"");
+
+ /* lookup the link target in the cache */
+ cache.for_each<Cached_rom&>([&] (Cached_rom &rom) {
+ if (!transfer.completed && rom.path == transfer.final_path) {
+ /*
+ * this session can be served but future requests to
+ * first_path must again traverse the symlinks to final_path
+ */
+ Session_component *session = new (heap)
+ Session_component(rom, sessions, transfer.request_id);
+ if (transfer.diag)
+ log("deliver cached \"", transfer.final_path, "\"");
+ env.parent().deliver_session_cap(
+ transfer.request_id, env.ep().manage(*session));
+ transfer.completed = true;
+ }
+ });
+ if (transfer.completed) {
+ destroy(heap, &transfer);
+ } else {
+ transfer.replace_handle(fs.node(transfer.final_path.string()));
+ resolve(transfer);
+ }
+ } else {
+ auto const pkt_seek = packet.position();
+
+ if (pkt_seek > transfer.seek || transfer.seek >= transfer.status.size) {
+ error("unexpected packet seek position for ", transfer.final_path);
+ error("packet seek is ", packet.position(), ", file seek is ", transfer.seek, ", file size is ", transfer.status.size);
+ transfer.seek = transfer.status.size;
+ } else {
+ size_t const n = min(packet.length(), transfer.status.size - pkt_seek);
+ memcpy(transfer.ram_ds.local_addr<char>()+pkt_seek,
+ fs.tx()->packet_content(packet), n);
+ transfer.seek = pkt_seek+n;
+ }
+
+ if (transfer.seek < transfer.status.size) {
+ transfer.submit_next_packet();
+ } else {
+ Cached_rom *rom = new (heap)
+ Cached_rom(env, rm, cache, transfer);
+ destroy(heap, &transfer);
+ Session_component *session = new (heap)
+ Session_component(*rom, sessions, transfer.request_id);
+ if (transfer.diag)
+ log("deliver fresh \"", transfer.final_path, "\"");
+ env.parent().deliver_session_cap(
+ transfer.request_id, env.ep().manage(*session));
+ session_requests.schedule();
+ }
+ }
+ }
+
+ /**
+ * Attempt a procedure with exception management
+ */
+ template <typename PROC>
+ void try_transfer(Path const &file_path, PROC const &proc)
{
using namespace File_system;
- try { return open(file_path); }
+ try { proc(); return; }
catch (Lookup_failed) { error(file_path, " not found"); }
catch (Invalid_handle) { error(file_path, ": invalid handle"); }
- catch (Invalid_name) { error(file_path, ": invalid nme"); }
+ catch (Invalid_name) { error(file_path, ": invalid name"); }
catch (Permission_denied) { error(file_path, ": permission denied"); }
catch (...) { error(file_path, ": unhandled error"); }
throw Service_denied();
@@ -364,55 +444,55 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
** Find ROM in cache **
***********************/
- Session_space::Id const id { pid.value };
-
Session_label const label = label_from_args(args.string());
Path const path(label.last_element().string());
- Cached_rom *rom = nullptr;
+ bool diag = session_diag_from_args(args.string()).enabled;
/* lookup the ROM in the cache */
+ Cached_rom *rom = nullptr;
cache.for_each<Cached_rom&>([&] (Cached_rom &other) {
if (!rom && other.path == path)
rom = &other;
});
- if (!rom) {
- File_system::File_handle handle = try_open(path);
- File_system::Handle_guard guard(fs, handle);
- size_t file_size = fs.status(handle).size;
-
- while (env.pd().avail_ram().value < file_size || env.pd().avail_caps().value < 8) {
- /* drop unused cache entries */
- if (!cache_evict()) break;
- }
-
- rom = new (heap) Cached_rom(cache, env, rm, path, file_size);
- }
-
- if (rom->completed()) {
+ if (rom) {
/* Create new RPC object */
Session_component *session = new (heap)
- Session_component(*rom, sessions, id, label);
- if (session_diag_from_args(args.string()).enabled)
- log("deliver ROM \"", label, "\"");
+ Session_component(*rom, sessions, pid);
+ if (diag)
+ log("deliver cached \"", path, "\" to \"", label, "\"");
env.parent().deliver_session_cap(pid, env.ep().manage(*session));
- } else if (!rom->transfer) {
- File_system::File_handle handle = try_open(path);
+ return;
+ }
+
+ /* find matching transfer */
+ bool pending = false;
+ transfers.for_each<Transfer&>([&] (Transfer &other) {
+ /* symlink race? */
+ pending |= other.matches(path);
+ });
+
+ if (pending) /* wait until transfer completes */
+ return;
+ /* initiate new transfer or throw Service_denied */
+ try_transfer(path, [&] () {
try {
- new (heap) Transfer(transfers, *rom, fs, handle, rom->file_size);
- }
- catch (...) {
- Genode::warning("defer transfer of ", rom->path);
- fs.close(handle);
- /* retry when next pending transfer completes */
+ Transfer *transfer = new (heap)
+ Transfer(env, fs, transfers, path, pid, diag);
+ resolve(*transfer);
+ } catch (Packet_alloc_failed) {
+ /* temporary failure */
return;
}
- }
+ });
}
+ /**
+ * Destroy a closed session component
+ */
void handle_session_close(Parent::Server::Id pid) override
{
Session_space::Id id { pid.value };
@@ -425,6 +505,9 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
});
}
+ /**
+ * Handle pending READ packets
+ */
void handle_packets()
{
Tx_source &source = *fs.tx();
@@ -439,12 +522,16 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
transfers.apply<Transfer&>(
Transfer_space::Id{pkt.handle().value}, [&] (Transfer &transfer)
{
- transfer.process_packet(pkt);
- if (transfer.completed()) {
- session_requests.schedule();
+ stray_pkt = false;
+ try {
+ try_transfer(transfer.final_path, [&] () {
+ process_packet(transfer, pkt);
+ });
+ } catch (Service_denied) {
+ env.parent().session_response(
+ transfer.request_id, Parent::SERVICE_DENIED);
destroy(heap, &transfer);
}
- stray_pkt = false;
});
if (stray_pkt)
--
2.28.0
From 4bff3638684e807a79a24e81bf59913849c9c100 Mon Sep 17 00:00:00 2001
From: Emery Hemingway <ehmry@posteo.net>
Date: Tue, 3 Nov 2020 17:37:50 +0100
Subject: [PATCH 2/2] cached_fs_rom: add directory session policy
Support for directing ROM requests into directories by policy.
---
repos/os/src/server/cached_fs_rom/README | 13 +++++++++++++
repos/os/src/server/cached_fs_rom/main.cc | 17 ++++++++++++++++-
2 files changed, 29 insertions(+), 1 deletion(-)
create mode 100644 repos/os/src/server/cached_fs_rom/README
diff --git a/repos/os/src/server/cached_fs_rom/README b/repos/os/src/server/cached_fs_rom/README
new file mode 100644
index 0000000000..855c20ba7c
--- /dev/null
+++ b/repos/os/src/server/cached_fs_rom/README
@@ -0,0 +1,13 @@
+The 'cached_fs_rom' server is an alternative to 'fs_rom' for serving ROMs
+of fixed-content. The server will cache file content between sessions and
+clients, however the server does not cache intermediate symlinks.
+
+Configuration
+~~~~~~~~~~~~~
+
+The only configuration option is the directory policy variable.
+
+! <config>
+! <policy label_suffix=".lib.so" directory="rom/lib" />
+! <default-policy directory="rom/bin" />
+! </config>
diff --git a/repos/os/src/server/cached_fs_rom/main.cc b/repos/os/src/server/cached_fs_rom/main.cc
index 5f2aea63f5..59dc2c7d98 100755
--- a/repos/os/src/server/cached_fs_rom/main.cc
+++ b/repos/os/src/server/cached_fs_rom/main.cc
@@ -13,6 +13,7 @@
/* Genode includes */
#include <os/path.h>
+#include <os/session_policy.h>
#include <file_system_session/connection.h>
#include <file_system/util.h>
#include <rom_session/rom_session.h>
@@ -267,9 +268,14 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
Session_requests_rom session_requests { env, *this };
+ Attached_rom_dataspace config_rom { env, "config" };
+
Io_signal_handler<Main> packet_handler {
env.ep(), *this, &Main::handle_packets };
+ Signal_handler<Attached_rom_dataspace> config_handler {
+ env.ep(), config_rom, &Attached_rom_dataspace::update };
+
/**
* Return true when a cache element is freed
*/
@@ -444,8 +450,17 @@ struct Cached_fs_rom::Main final : Genode::Session_request_handler
** Find ROM in cache **
***********************/
+
+ Path path("/");
Session_label const label = label_from_args(args.string());
- Path const path(label.last_element().string());
+
+ try {
+ Session_policy policy(label, config_rom.xml());
+ path.append(policy.attribute_value(
+ "directory", String<Path::capacity()>("/")).string());
+ } catch (Service_denied) { }
+
+ path.append_element(label.last_element().string());
bool diag = session_diag_from_args(args.string()).enabled;
--
2.28.0