mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-22 15:02:25 +00:00
cf87b0fadb
This patch removes the 'Insufficient_buffer' exception by returning the WRITE_ERR_WOULD_BLOCK result value instead. It also eliminates the superfluous WRITE_ERR_AGAIN and WRITE_ERR_INTERRUPT codes. Issue #4697
312 lines
6.7 KiB
C++
312 lines
6.7 KiB
C++
/*
|
|
* \brief Integration of the Consistent Block Encrypter (CBE)
|
|
* \author Martin Stein
|
|
* \author Josef Soentgen
|
|
* \date 2020-11-10
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 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.
|
|
*/
|
|
|
|
#ifndef _CBE__VFS__IO_JOB_H_
|
|
#define _CBE__VFS__IO_JOB_H_
|
|
|
|
/* Genode includes */
|
|
#include <vfs/types.h>
|
|
#include <vfs/vfs_handle.h>
|
|
|
|
namespace Util {
|
|
|
|
using file_size = Vfs::file_size;
|
|
using file_offset = Vfs::file_offset;
|
|
|
|
struct Io_job
|
|
{
|
|
struct Buffer
|
|
{
|
|
char *base;
|
|
file_size size;
|
|
};
|
|
|
|
enum class Operation { INVALID, READ, WRITE, SYNC };
|
|
|
|
static char const *to_string(Operation op)
|
|
{
|
|
using Op = Operation;
|
|
|
|
switch (op) {
|
|
case Op::READ: return "READ";
|
|
case Op::WRITE: return "WRITE";
|
|
case Op::SYNC: return "SYNC";
|
|
default: return "INVALID";
|
|
}
|
|
}
|
|
|
|
struct Unsupported_Operation : Genode::Exception { };
|
|
struct Invalid_state : Genode::Exception { };
|
|
|
|
enum State { PENDING, IN_PROGRESS, COMPLETE, };
|
|
|
|
static State _initial_state(Operation const op)
|
|
{
|
|
using Op = Operation;
|
|
|
|
switch (op) {
|
|
case Op::READ: return State::PENDING;
|
|
case Op::WRITE: return State::PENDING;
|
|
case Op::SYNC: return State::PENDING;
|
|
default: throw Unsupported_Operation();
|
|
}
|
|
}
|
|
|
|
static char const *_state_to_string(State s)
|
|
{
|
|
switch (s) {
|
|
case State::PENDING: return "PENDING";
|
|
case State::IN_PROGRESS: return "IN_PROGRESS";
|
|
case State::COMPLETE: return "COMPLETE";
|
|
}
|
|
|
|
throw Invalid_state();
|
|
}
|
|
|
|
enum class Partial_result { ALLOW, DENY };
|
|
|
|
Vfs::Vfs_handle &_handle;
|
|
|
|
Operation const _op;
|
|
State _state;
|
|
char *_data;
|
|
file_offset const _base_offset;
|
|
file_offset _current_offset;
|
|
file_size _current_count;
|
|
|
|
bool const _allow_partial;
|
|
|
|
bool _success;
|
|
bool _complete;
|
|
|
|
bool _read()
|
|
{
|
|
bool progress = false;
|
|
|
|
switch (_state) {
|
|
case State::PENDING:
|
|
|
|
_handle.seek(_base_offset + _current_offset);
|
|
if (!_handle.fs().queue_read(&_handle, _current_count)) {
|
|
return progress;
|
|
}
|
|
|
|
_state = State::IN_PROGRESS;
|
|
progress = true;
|
|
[[fallthrough]];
|
|
case State::IN_PROGRESS:
|
|
{
|
|
using Result = Vfs::File_io_service::Read_result;
|
|
|
|
bool completed = false;
|
|
file_size out = 0;
|
|
|
|
Result const result =
|
|
_handle.fs().complete_read(&_handle,
|
|
_data + _current_offset,
|
|
_current_count, out);
|
|
if ( result == Result::READ_QUEUED
|
|
|| result == Result::READ_ERR_INTERRUPT
|
|
|| result == Result::READ_ERR_AGAIN
|
|
|| result == Result::READ_ERR_WOULD_BLOCK) {
|
|
return progress;
|
|
} else
|
|
|
|
if (result == Result::READ_OK) {
|
|
_current_offset += out;
|
|
_current_count -= out;
|
|
_success = true;
|
|
} else
|
|
|
|
if ( result == Result::READ_ERR_IO
|
|
|| result == Result::READ_ERR_INVALID) {
|
|
_success = false;
|
|
completed = true;
|
|
}
|
|
|
|
if (_current_count == 0 || completed || (out == 0 && _allow_partial)) {
|
|
_state = State::COMPLETE;
|
|
} else {
|
|
_state = State::PENDING;
|
|
/* partial read, keep trying */
|
|
return true;
|
|
}
|
|
progress = true;
|
|
}
|
|
[[fallthrough]];
|
|
case State::COMPLETE:
|
|
|
|
_complete = true;
|
|
progress = true;
|
|
default: break;
|
|
}
|
|
|
|
return progress;
|
|
}
|
|
|
|
bool _write()
|
|
{
|
|
bool progress = false;
|
|
|
|
switch (_state) {
|
|
case State::PENDING:
|
|
|
|
_handle.seek(_base_offset + _current_offset);
|
|
|
|
_state = State::IN_PROGRESS;
|
|
progress = true;
|
|
[[fallthrough]];
|
|
case State::IN_PROGRESS:
|
|
{
|
|
using Result = Vfs::File_io_service::Write_result;
|
|
|
|
bool completed = false;
|
|
file_size out = 0;
|
|
|
|
|
|
Result const result =
|
|
_handle.fs().write(&_handle, _data + _current_offset,
|
|
_current_count, out);
|
|
switch (result) {
|
|
case Result::WRITE_ERR_WOULD_BLOCK:
|
|
return progress;
|
|
|
|
case Result::WRITE_OK:
|
|
_current_offset += out;
|
|
_current_count -= out;
|
|
_success = true;
|
|
break;
|
|
|
|
case Result::WRITE_ERR_IO:
|
|
case Result::WRITE_ERR_INVALID:
|
|
_success = false;
|
|
completed = true;
|
|
break;
|
|
}
|
|
|
|
if (_current_count == 0 || completed || (out == 0 && _allow_partial)) {
|
|
_state = State::COMPLETE;
|
|
} else {
|
|
_state = State::PENDING;
|
|
/* partial write, keep trying */
|
|
return true;
|
|
}
|
|
progress = true;
|
|
}
|
|
[[fallthrough]];
|
|
case State::COMPLETE:
|
|
|
|
_complete = true;
|
|
progress = true;
|
|
default: break;
|
|
}
|
|
|
|
return progress;
|
|
}
|
|
|
|
bool _sync()
|
|
{
|
|
bool progress = false;
|
|
|
|
switch (_state) {
|
|
case State::PENDING:
|
|
|
|
if (!_handle.fs().queue_sync(&_handle)) {
|
|
return progress;
|
|
}
|
|
_state = State::IN_PROGRESS;
|
|
progress = true;
|
|
[[fallthrough]];
|
|
case State::IN_PROGRESS:
|
|
{
|
|
using Result = Vfs::File_io_service::Sync_result;
|
|
Result const result = _handle.fs().complete_sync(&_handle);
|
|
|
|
if (result == Result::SYNC_QUEUED) {
|
|
return progress;
|
|
} else
|
|
|
|
if (result == Result::SYNC_ERR_INVALID) {
|
|
_success = false;
|
|
} else
|
|
|
|
if (result == Result::SYNC_OK) {
|
|
_success = true;
|
|
}
|
|
|
|
_state = State::COMPLETE;
|
|
progress = true;
|
|
}
|
|
[[fallthrough]];
|
|
case State::COMPLETE:
|
|
|
|
_complete = true;
|
|
progress = true;
|
|
default: break;
|
|
}
|
|
|
|
return progress;
|
|
}
|
|
|
|
Io_job(Vfs::Vfs_handle &handle,
|
|
Operation op,
|
|
Buffer &buffer,
|
|
file_offset base_offset,
|
|
Partial_result partial_result = Partial_result::DENY)
|
|
:
|
|
_handle { handle },
|
|
_op { op },
|
|
_state { _initial_state(op) },
|
|
_data { buffer.base },
|
|
_base_offset { base_offset },
|
|
_current_offset { 0 },
|
|
_current_count { buffer.size },
|
|
_allow_partial { partial_result == Partial_result::ALLOW },
|
|
_success { false },
|
|
_complete { false }
|
|
{ }
|
|
|
|
bool completed() const { return _complete; }
|
|
bool succeeded() const { return _success; }
|
|
Operation op() const { return _op; }
|
|
|
|
void print(Genode::Output &out) const
|
|
{
|
|
Genode::print(out, "(", to_string(_op), ")",
|
|
" state: ", _state_to_string(_state),
|
|
" current_offset: ", _current_offset,
|
|
" current_count: ", _current_count,
|
|
" success: ", _success,
|
|
" complete: ", _complete);
|
|
}
|
|
|
|
bool execute()
|
|
{
|
|
using Op = Operation;
|
|
|
|
switch (_op) {
|
|
case Op::READ: return _read();
|
|
case Op::WRITE: return _write();
|
|
case Op::SYNC: return _sync();
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
Genode::size_t current_offset() const { return _current_offset; }
|
|
};
|
|
|
|
} /* namespace Util */
|
|
|
|
#endif /* _CBE__VFS__IO_JOB_H_ */
|