genode/repos/gems/include/cbe/vfs/io_job.h
Norman Feske cf87b0fadb vfs: simplify File_io_service::write return values
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
2023-01-24 12:07:28 +01:00

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_ */