genode/repos/os/include/file_system_session/file_system_session.h
Norman Feske 4d442bca30 Streamline exception types
This patch reduces the number of exception types by facilitating
globally defined exceptions for common usage patterns shared by most
services. In particular, RPC functions that demand a session-resource
upgrade not longer reflect this condition via a session-specific
exception but via the 'Out_of_ram' or 'Out_of_caps' types.

Furthermore, the 'Parent::Service_denied', 'Parent::Unavailable',
'Root::Invalid_args', 'Root::Unavailable', 'Service::Invalid_args',
'Service::Unavailable', and 'Local_service::Factory::Denied' types have
been replaced by the single 'Service_denied' exception type defined in
'session/session.h'.

This consolidation eases the error handling (there are fewer exceptions
to handle), alleviates the need to convert exceptions along the
session-creation call chain, and avoids possible aliasing problems
(catching the wrong type with the same name but living in a different
scope).
2017-05-31 13:16:07 +02:00

433 lines
13 KiB
C++

/*
* \brief File-system session interface
* \author Norman Feske
* \author Emery Hemingway
* \author Christian Helmuth
* \date 2012-04-05
*/
/*
* Copyright (C) 2012-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__FILE_SYSTEM_SESSION__FILE_SYSTEM_SESSION_H_
#define _INCLUDE__FILE_SYSTEM_SESSION__FILE_SYSTEM_SESSION_H_
#include <base/exception.h>
#include <os/packet_stream.h>
#include <packet_stream_tx/packet_stream_tx.h>
#include <session/session.h>
namespace File_system {
struct Node_handle;
struct File_handle;
struct Dir_handle;
struct Symlink_handle;
using Genode::size_t;
typedef Genode::uint64_t seek_off_t;
typedef Genode::uint64_t file_size_t;
typedef Genode::Out_of_ram Out_of_ram;
typedef Genode::Out_of_caps Out_of_caps;
class Packet_descriptor;
/**
* Flags as supplied to 'file', 'dir', and 'symlink' calls
*/
enum Mode { STAT_ONLY = 0, READ_ONLY = 1, WRITE_ONLY = 2, READ_WRITE = 3 };
enum { MAX_NAME_LEN = 256, MAX_PATH_LEN = 1024 };
/**
* File offset constant for reading or writing to the end of a file
*
* Clients are unable to reliably append to the end of a file where there
* may be other writes to the same offset in the queues of other clients.
* The SEEK_TAIL constant resolves this contention by aligning packet
* operations with the end of the file at the time the packet is dequeued.
*
* SEEK_TAIL behavior with directory and symlink nodes is undefined.
*/
enum { SEEK_TAIL = ~0ULL };
typedef Genode::Rpc_in_buffer<MAX_NAME_LEN> Name;
typedef Genode::Rpc_in_buffer<MAX_PATH_LEN> Path;
struct Status;
struct Control;
struct Directory_entry;
/*
* Exception types
*/
class Exception : public Genode::Exception { };
class Invalid_handle : Exception { };
class Invalid_name : Exception { };
class Lookup_failed : Exception { };
class Name_too_long : Exception { };
class Node_already_exists : Exception { };
class No_space : Exception { };
class Not_empty : Exception { };
class Permission_denied : Exception { };
struct Session;
}
struct File_system::Node_handle
{
unsigned long value;
Node_handle() : value(~0UL) { }
Node_handle(int v) : value(v) { }
bool valid() const { return value != ~0UL; }
bool operator == (Node_handle const &other) const { return other.value == value; }
bool operator != (Node_handle const &other) const { return other.value != value; }
};
struct File_system::File_handle : Node_handle
{
File_handle() { }
File_handle(unsigned long v) : Node_handle(v) { }
};
struct File_system::Dir_handle : Node_handle
{
Dir_handle() { }
Dir_handle(unsigned long v) : Node_handle(v) { }
};
struct File_system::Symlink_handle : Node_handle
{
Symlink_handle() { }
Symlink_handle(unsigned long v) : Node_handle(v) { }
};
class File_system::Packet_descriptor : public Genode::Packet_descriptor
{
public:
enum Opcode { READ, WRITE, CONTENT_CHANGED, READ_READY };
private:
Node_handle _handle; /* node handle */
Opcode _op; /* requested operation */
seek_off_t _position; /* file seek offset in bytes */
size_t _length; /* transaction length in bytes */
bool _success; /* indicates success of operation */
public:
/**
* Constructor
*/
Packet_descriptor(Genode::off_t buf_offset = 0,
Genode::size_t buf_size = 0)
:
Genode::Packet_descriptor(buf_offset, buf_size), _handle(-1),
_op(READ), _position(0), _length(0), _success(false) { }
/**
* Constructor
*
* \param position seek offset in bytes
*
* Note, if 'position' is set to 'SEEK_TAIL' read operations will read
* 'length' bytes from the end of the file while write operations will
* append length bytes at the end of the file.
*/
Packet_descriptor(Packet_descriptor p,
Node_handle handle, Opcode op, size_t length,
seek_off_t position = SEEK_TAIL)
:
Genode::Packet_descriptor(p.offset(), p.size()),
_handle(handle), _op(op),
_position(position), _length(length), _success(false)
{ }
/**
* Constructor
*
* This constructor provided for sending server-side
* notification packets.
*/
Packet_descriptor(Node_handle handle, Opcode op)
:
Genode::Packet_descriptor(0, 0),
_handle(handle), _op(op),
_position(0), _length(0), _success(true)
{ }
Node_handle handle() const { return _handle; }
Opcode operation() const { return _op; }
seek_off_t position() const { return _position; }
size_t length() const { return _length; }
bool succeeded() const { return _success; }
/*
* Accessors called at the server side
*/
void succeeded(bool b) { _success = b ? 1 : 0; }
void length(size_t length) { _length = length; }
};
struct File_system::Status
{
enum {
MODE_SYMLINK = 0020000,
MODE_FILE = 0100000,
MODE_DIRECTORY = 0040000,
};
/*
* XXX add access time
* XXX add executable bit
*/
file_size_t size;
unsigned mode;
unsigned long inode;
/**
* Return true if node is a directory
*/
bool directory() const { return mode & MODE_DIRECTORY; }
/**
* Return true if node is a symbolic link
*/
bool symlink() const { return mode & MODE_SYMLINK; }
/**
* Return true if node is a directory
*
* \deprecated use 'directory' instead
*/
bool is_directory() const { return directory(); }
/**
* Return true if node is a symbolic link
*
* \deprecated use 'symlink' instead
*/
bool is_symlink() const { return symlink(); }
};
struct File_system::Control { /* to manipulate the executable bit */ };
/**
* Data structure returned when reading from a directory node
*/
struct File_system::Directory_entry
{
enum Type { TYPE_FILE, TYPE_DIRECTORY, TYPE_SYMLINK };
unsigned long inode;
Type type;
char name[MAX_NAME_LEN];
};
struct File_system::Session : public Genode::Session
{
enum { TX_QUEUE_SIZE = 16 };
typedef Genode::Packet_stream_policy<File_system::Packet_descriptor,
TX_QUEUE_SIZE, TX_QUEUE_SIZE,
char> Tx_policy;
typedef Packet_stream_tx::Channel<Tx_policy> Tx;
static const char *service_name() { return "File_system"; }
enum { CAP_QUOTA = 5 };
virtual ~Session() { }
/**
* Request client-side packet-stream interface of tx channel
*/
virtual Tx::Source *tx() { return 0; }
/**
* Open or create file
*
* \throw Invalid_handle directory handle is invalid
* \throw Invalid_name file name contains invalid characters
* \throw Lookup_failed the name refers to a node other than a file
* \throw Node_already_exists file cannot be created because a node with
* the same name already exists
* \throw No_space storage exhausted
* \throw Out_of_ram server cannot allocate metadata
* \throw Out_of_caps
* \throw Permission_denied
*/
virtual File_handle file(Dir_handle, Name const &name, Mode, bool create) = 0;
/**
* Open or create symlink
*
* \throw Invalid_handle directory handle is invalid
* \throw Invalid_name symlink name contains invalid characters
* \throw Lookup_failed the name refers to a node other than a symlink
* \throw Node_already_exists symlink cannot be created because a node with
* the same name already exists
* \throw No_space storage exhausted
* \throw Out_of_ram server cannot allocate metadata
* \throw Out_of_caps
* \throw Permission_denied
*/
virtual Symlink_handle symlink(Dir_handle, Name const &name, bool create) = 0;
/**
* Open or create directory
*
* \throw Lookup_failed path lookup failed because one element
* of 'path' does not exist
* \throw Name_too_long 'path' is too long
* \throw Node_already_exists directory cannot be created because a
* node with the same name already exists
* \throw No_space storage exhausted
* \throw Out_of_ram server cannot allocate metadata
* \throw Out_of_caps
* \throw Permission_denied
*/
virtual Dir_handle dir(Path const &path, bool create) = 0;
/**
* Open existing node
*
* The returned node handle can be used merely as argument for
* 'status'.
*
* \throw Lookup_failed path lookup failed because one element
* of 'path' does not exist
* \throw Out_of_ram server cannot allocate metadata
* \throw Out_of_caps
*/
virtual Node_handle node(Path const &path) = 0;
/**
* Close file
*/
virtual void close(Node_handle) = 0;
/**
* Request information about an open file or directory
*/
virtual Status status(Node_handle) = 0;
/**
* Set information about an open file or directory
*/
virtual void control(Node_handle, Control) = 0;
/**
* Delete file or directory
*
* \throw Invalid_handle directory handle is invalid
* \throw Invalid_name 'name' contains invalid characters
* \throw Lookup_failed lookup of 'name' in 'dir' failed
* \throw Not_empty argument is a non-empty directory and
* the backend does not support recursion
* \throw Permission_denied
*/
virtual void unlink(Dir_handle dir, Name const &name) = 0;
/**
* Truncate or grow file to specified size
*
* \throw Invalid_handle node handle is invalid
* \throw No_space new size exceeds free space
* \throw Permission_denied node modification not allowed
*/
virtual void truncate(File_handle, file_size_t size) = 0;
/**
* Move and rename directory entry
*
* \throw Invalid_handle a directory handle is invalid
* \throw Invalid_name 'to' contains invalid characters
* \throw Lookup_failed 'from' not found
* \throw Permission_denied node modification not allowed
*/
virtual void move(Dir_handle, Name const &from,
Dir_handle, Name const &to) = 0;
/**
* Synchronize file system
*
* This is only needed by file systems that maintain an internal
* cache, which needs to be flushed on certain occasions.
*/
virtual void sync(Node_handle) { }
/*******************
** RPC interface **
*******************/
GENODE_RPC(Rpc_tx_cap, Genode::Capability<Tx>, _tx_cap);
GENODE_RPC_THROW(Rpc_file, File_handle, file,
GENODE_TYPE_LIST(Invalid_handle, Invalid_name,
Lookup_failed, Node_already_exists,
No_space, Out_of_ram, Out_of_caps,
Permission_denied),
Dir_handle, Name const &, Mode, bool);
GENODE_RPC_THROW(Rpc_symlink, Symlink_handle, symlink,
GENODE_TYPE_LIST(Invalid_handle, Invalid_name,
Lookup_failed, Node_already_exists,
No_space, Out_of_ram, Out_of_caps,
Permission_denied),
Dir_handle, Name const &, bool);
GENODE_RPC_THROW(Rpc_dir, Dir_handle, dir,
GENODE_TYPE_LIST(Lookup_failed, Name_too_long,
Node_already_exists, No_space,
Out_of_ram, Out_of_caps, Permission_denied),
Path const &, bool);
GENODE_RPC_THROW(Rpc_node, Node_handle, node,
GENODE_TYPE_LIST(Lookup_failed, Out_of_ram, Out_of_caps),
Path const &);
GENODE_RPC(Rpc_close, void, close, Node_handle);
GENODE_RPC(Rpc_status, Status, status, Node_handle);
GENODE_RPC(Rpc_control, void, control, Node_handle, Control);
GENODE_RPC_THROW(Rpc_unlink, void, unlink,
GENODE_TYPE_LIST(Invalid_handle, Invalid_name,
Lookup_failed, Not_empty,
Permission_denied),
Dir_handle, Name const &);
GENODE_RPC_THROW(Rpc_truncate, void, truncate,
GENODE_TYPE_LIST(Invalid_handle, No_space,
Permission_denied),
File_handle, file_size_t);
GENODE_RPC_THROW(Rpc_move, void, move,
GENODE_TYPE_LIST(Invalid_handle, Invalid_name,
Lookup_failed, Permission_denied),
Dir_handle, Name const &, Dir_handle, Name const &);
GENODE_RPC(Rpc_sync, void, sync, Node_handle);
GENODE_RPC_INTERFACE(Rpc_tx_cap, Rpc_file, Rpc_symlink, Rpc_dir, Rpc_node,
Rpc_close, Rpc_status, Rpc_control, Rpc_unlink,
Rpc_truncate, Rpc_move, Rpc_sync);
};
#endif /* _INCLUDE__FILE_SYSTEM_SESSION__FILE_SYSTEM_SESSION_H_ */