mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-25 16:31:06 +00:00
4c4ce2f899
The former implementation did not internally track ROM changes notified vs. delivered to the client. We adapt the versioning implementation implemented in dynamic_rom_session.h and enable explicit notification of the current version. The feature is used by the clipboard to notify permitted readers of the clipboard ROM service on focus change via the newly created private Rom::Module::_notify_permitted_readers() function. Fixes #4274
327 lines
8.0 KiB
C++
327 lines
8.0 KiB
C++
/*
|
|
* \brief ROM module written by report service, read by ROM service
|
|
* \author Norman Feske
|
|
* \date 2014-01-11
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014-2017 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.
|
|
*/
|
|
|
|
#ifndef _INCLUDE__REPORT_ROM__ROM_MODULE_H_
|
|
#define _INCLUDE__REPORT_ROM__ROM_MODULE_H_
|
|
|
|
/* Genode includes */
|
|
#include <util/reconstructible.h>
|
|
#include <os/session_policy.h>
|
|
#include <base/attached_ram_dataspace.h>
|
|
|
|
namespace Rom {
|
|
using Genode::size_t;
|
|
using Genode::Constructible;
|
|
using Genode::Attached_ram_dataspace;
|
|
using Genode::Interface;
|
|
|
|
class Module;
|
|
class Readable_module;
|
|
class Registry;
|
|
class Writer;
|
|
class Reader;
|
|
class Buffer;
|
|
|
|
typedef Genode::List<Module> Module_list;
|
|
typedef Genode::List<Reader> Reader_list;
|
|
typedef Genode::List<Writer> Writer_list;
|
|
}
|
|
|
|
|
|
struct Rom::Writer : private Writer_list::Element, Interface
|
|
{
|
|
friend class Genode::List<Writer>;
|
|
using Writer_list::Element::next;
|
|
|
|
virtual Genode::Session_label label() const = 0;
|
|
};
|
|
|
|
|
|
struct Rom::Reader : private Reader_list::Element, Interface
|
|
{
|
|
friend class Genode::List<Reader>;
|
|
using Reader_list::Element::next;
|
|
|
|
virtual void mark_as_outdated() = 0;
|
|
virtual void mark_as_invalidated() = 0;
|
|
virtual void notify_client() = 0;
|
|
};
|
|
|
|
|
|
struct Rom::Readable_module : Interface
|
|
{
|
|
/**
|
|
* Exception type
|
|
*/
|
|
class Buffer_too_small { };
|
|
|
|
/**
|
|
* Read content of ROM module
|
|
*
|
|
* Called by ROM service when a dataspace is obtained by the client.
|
|
*
|
|
* \throw Buffer_too_small
|
|
*/
|
|
virtual size_t read_content(Reader const &reader, char *dst,
|
|
size_t dst_len) const = 0;
|
|
|
|
virtual size_t size() const = 0;
|
|
};
|
|
|
|
|
|
/**
|
|
* A Rom::Module gets created as soon as either a ROM client or a Report client
|
|
* refers to it.
|
|
*
|
|
* XXX We never know which of both types of client is actually connected. How
|
|
* should pay for it? There are two choices: The backing store could be paid
|
|
* by the server, thereby exposing the server to possibe resource exhaustion
|
|
* triggered by a malicious client. Alternatively, we could make all clients of
|
|
* either kind of service pay that refer to the Rom::Module. In the worst case,
|
|
* however, if there are many client for a single report, the paid-for RAM
|
|
* quota will never be used. For now, we simply allocate the backing store from
|
|
* the server's quota.
|
|
*
|
|
* The Rom::Module gets destroyed when no client refers to it anymore.
|
|
*/
|
|
struct Rom::Module : private Module_list::Element, Readable_module
|
|
{
|
|
private:
|
|
|
|
friend class Genode::List<Module>;
|
|
|
|
/*
|
|
* Noncopyable
|
|
*/
|
|
Module(Module const &);
|
|
Module &operator = (Module const &);
|
|
|
|
public:
|
|
|
|
using Module_list::Element::next;
|
|
|
|
typedef Genode::String<200> Name;
|
|
|
|
struct Read_policy : Interface
|
|
{
|
|
/**
|
|
* Return true if the reader is allowed to read the module content
|
|
*/
|
|
virtual bool read_permitted(Module const &,
|
|
Writer const &, Reader const &) const = 0;
|
|
};
|
|
|
|
struct Write_policy : Interface
|
|
{
|
|
/**
|
|
* Return true of the writer is permitted to write content
|
|
*
|
|
* This policy hook can be used to implement dynamic policies
|
|
* as employed by the clipboard, which blocks reports from
|
|
* unfocused clients.
|
|
*/
|
|
virtual bool write_permitted(Module const &, Writer const &) const = 0;
|
|
};
|
|
|
|
private:
|
|
|
|
Name _name;
|
|
|
|
Genode::Ram_allocator &_ram;
|
|
Genode::Region_map &_rm;
|
|
|
|
Read_policy const &_read_policy;
|
|
Write_policy const &_write_policy;
|
|
|
|
Reader_list mutable _readers { };
|
|
Writer_list mutable _writers { };
|
|
|
|
/**
|
|
* Origin of the content currently stored in the module
|
|
*/
|
|
Writer const *_last_writer = nullptr;
|
|
|
|
/**
|
|
* Dataspace used as backing store
|
|
*
|
|
* The buffer for the content is not allocated from the heap to
|
|
* allow for the immediate release of the underlying backing store when
|
|
* the module gets destructed.
|
|
*/
|
|
Constructible<Attached_ram_dataspace> _ds { };
|
|
|
|
/**
|
|
* Content size, which may less than the capacilty of '_ds'.
|
|
*/
|
|
size_t _size = 0;
|
|
|
|
|
|
/********************************
|
|
** Interface used by registry **
|
|
********************************/
|
|
|
|
friend class Registry;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* \param ram allocator for the module's backing store
|
|
* \param rm region map of the local address space, needed
|
|
* to access the allocated backing store
|
|
* \param name module name
|
|
* \param read_policy policy hook function that is evaluated each
|
|
* time when the module content is obtained
|
|
* \param write_policy policy hook function that is evaluated each
|
|
* time when the module content is changed
|
|
*/
|
|
Module(Genode::Ram_allocator &ram,
|
|
Genode::Region_map &rm,
|
|
Name const &name,
|
|
Read_policy const &read_policy,
|
|
Write_policy const &write_policy)
|
|
:
|
|
_name(name), _ram(ram), _rm(rm),
|
|
_read_policy(read_policy), _write_policy(write_policy)
|
|
{ }
|
|
|
|
|
|
/*************************************************
|
|
** Interface to be used by the 'Registry' only **
|
|
*************************************************/
|
|
|
|
bool _reader_registered(Reader const &reader) const
|
|
{
|
|
for (Reader const *r = _readers.first(); r; r = r->next())
|
|
if (r == &reader)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void _register(Reader &reader) { _readers.insert(&reader); }
|
|
|
|
void _unregister(Reader &reader) { _readers.remove(&reader); }
|
|
|
|
void _register(Writer &writer)
|
|
{
|
|
_writers.insert(&writer);
|
|
}
|
|
|
|
void _unregister(Writer const &writer)
|
|
{
|
|
_writers.remove(&writer);
|
|
|
|
/* clear content if its origin disappears */
|
|
if (_last_writer == &writer) {
|
|
Genode::memset(_ds->local_addr<char>(), 0, _size);
|
|
_size = 0;
|
|
_last_writer = nullptr;
|
|
}
|
|
}
|
|
|
|
bool _has_name(Name const &name) const { return name == _name; }
|
|
|
|
bool _in_use() const
|
|
{
|
|
return _readers.first() || _writers.first();
|
|
}
|
|
|
|
unsigned _num_writers() const
|
|
{
|
|
unsigned cnt = 0;
|
|
for (Writer const *w = _writers.first(); w; w = w->next())
|
|
cnt++;
|
|
|
|
return cnt;
|
|
}
|
|
|
|
void _notify_permitted_readers()
|
|
{
|
|
if (!_last_writer)
|
|
return;
|
|
|
|
for (Reader *r = _readers.first(); r; r = r->next()) {
|
|
if (_read_policy.read_permitted(*this, *_last_writer, *r))
|
|
r->notify_client();
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Assign new content to the ROM module
|
|
*
|
|
* Called by report service when a new report comes in.
|
|
*/
|
|
void write_content(Writer const &writer, char const * const src, size_t const src_len)
|
|
{
|
|
if (!_write_policy.write_permitted(*this, writer))
|
|
return;
|
|
|
|
_size = 0;
|
|
|
|
_last_writer = &writer;
|
|
|
|
/*
|
|
* Realloc backing store if needed
|
|
*
|
|
* Take a terminating zero into account, which we append to each
|
|
* report. This way, we do not need to trust report clients to
|
|
* append a zero termination to textual reports.
|
|
*/
|
|
if (!_ds.constructed() || _ds->size() < (src_len + 1))
|
|
_ds.construct(_ram, _rm, (src_len + 1));
|
|
|
|
/* copy content into backing store */
|
|
_size = src_len;
|
|
Genode::memcpy(_ds->local_addr<char>(), src, _size);
|
|
|
|
/* append zero termination */
|
|
_ds->local_addr<char>()[src_len] = 0;
|
|
|
|
/* notify ROM clients that access the module */
|
|
for (Reader *r = _readers.first(); r; r = r->next()) {
|
|
|
|
if (_read_policy.read_permitted(*this, *_last_writer, *r))
|
|
r->mark_as_outdated();
|
|
else
|
|
r->mark_as_invalidated();
|
|
r->notify_client();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Readable_module interface
|
|
*/
|
|
size_t read_content(Reader const &reader, char *dst, size_t dst_len) const override
|
|
{
|
|
if (!_ds.constructed() || !_last_writer)
|
|
return 0;
|
|
|
|
if (!_read_policy.read_permitted(*this, *_last_writer, reader))
|
|
return 0;
|
|
|
|
if (dst_len < _size)
|
|
throw Buffer_too_small();
|
|
|
|
Genode::memcpy(dst, _ds->local_addr<char>(), _size);
|
|
return _size;
|
|
}
|
|
|
|
virtual size_t size() const override { return _size; }
|
|
|
|
Name name() const { return _name; }
|
|
};
|
|
|
|
#endif /* _INCLUDE__REPORT_ROM__ROM_MODULE_H_ */
|