Files
genode/repos/os/include/file_system_session/file_system_session.h
Norman Feske d7b1a89087 file-system session: use same dirent size as VFS
This change allows for the conversion of directory entries in place
(i.e., in the VFS server) and anticipates the potential future merge of
both types into one.

Issue #3547
2019-11-19 14:54:13 +01:00

537 lines
16 KiB
C++

/*
* \brief File-system session interface
* \author Norman Feske
* \author Emery Hemingway
* \author Christian Helmuth
* \date 2012-04-05
*/
/*
* Copyright (C) 2012-2018 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 <util/string.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
{
typedef Genode::Id_space<Node>::Id Id;
};
struct File : Node
{
struct Id : Node::Id
{
explicit Id(unsigned long value) : Node::Id { value } { };
};
};
struct Directory : Node
{
struct Id : Node::Id
{
explicit Id(unsigned long value) : Node::Id { value } { };
};
};
struct Symlink : Node
{
struct Id : Node::Id
{
explicit Id(unsigned long value) : Node::Id { value } { };
};
};
struct Watch : Node
{
struct Id : Node::Id
{
explicit Id(unsigned long value) : Node::Id { value } { };
};
};
typedef Node::Id Node_handle;
typedef File::Id File_handle;
typedef Directory::Id Dir_handle;
typedef Symlink::Id Symlink_handle;
typedef Watch::Id Watch_handle;
enum class Node_type {
DIRECTORY,
SYMLINK,
CONTINUOUS_FILE,
TRANSACTIONAL_FILE
};
struct Node_rwx
{
bool readable;
bool writeable;
bool executable;
};
using Genode::size_t;
typedef Genode::uint64_t seek_off_t;
typedef Genode::uint64_t file_size_t;
struct Timestamp
{
/*
* The INVALID value is used whenever the underlying file system
* session does not support modification timestamps. The value is
* chosen such that it is unlikely to occur, instead of simply '0',
* which would correspond to plausible time (see comment below).
* This allows for handling this case explicitly. In any case, an
* invalid timestamp should not be used for doing any calculations.
*/
static constexpr Genode::int64_t INVALID = 0x7fffffffffffffffLL;
/*
* The 'value' member contains the modification timestamp in seconds.
* Value '0' is defined as 1970-01-01T00:00:00Z, where a positive value
* covers all seconds after this date and a negative one all before.
*/
Genode::int64_t value;
};
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 = 128, 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 { };
class Unavailable : Exception { };
struct Session;
}
class File_system::Packet_descriptor : public Genode::Packet_descriptor
{
public:
enum Opcode {
READ,
WRITE,
WRITE_TIMESTAMP,
CONTENT_CHANGED,
READ_READY,
/**
* Synchronize file system
*
* This is only needed by file systems that maintain an internal
* cache, which needs to be flushed on certain occasions.
*/
SYNC
};
private:
Node_handle _handle { 0 }; /* node handle */
Opcode _op; /* requested operation */
bool _success; /* indicates success of operation */
union
{
struct
{
seek_off_t _position; /* file seek offset in bytes */
size_t _length; /* transaction length in bytes */
};
Timestamp _modification_time; /* seconds since the Unix epoch */
};
public:
/**
* Constructor
*/
Packet_descriptor(Genode::off_t buf_offset = 0,
Genode::size_t buf_size = 0)
:
Genode::Packet_descriptor(buf_offset, buf_size),
_op(READ), _success(false), _position(0), _length(0) { }
/**
* 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), _success(false),
_position(position), _length(length)
{ }
/**
* 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), _success(true),
_position(0), _length(0)
{ }
/**
* Constructor
*/
Packet_descriptor(Packet_descriptor p, Node_handle handle, Opcode op, Timestamp const &mtime)
:
Genode::Packet_descriptor(p.offset(), p.size()),
_handle(handle), _op(op), _success(false),
_modification_time(mtime)
{ }
Node_handle handle() const { return _handle; }
Opcode operation() const { return _op; }
seek_off_t position() const { return _op != Opcode::WRITE_TIMESTAMP ? _position : 0; }
size_t length() const { return _op != Opcode::WRITE_TIMESTAMP ? _length : 0; }
bool succeeded() const { return _success; }
template <typename FN>
void with_timestamp(FN const &fn) const
{
if (_op == Opcode::WRITE_TIMESTAMP)
fn(_modification_time);
}
/*
* 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
{
file_size_t size;
Node_type type;
Node_rwx rwx;
unsigned long inode;
Timestamp modification_time;
/**
* Return true if node is a directory
*/
bool directory() const { return type == Node_type::DIRECTORY; }
/**
* Return true if node is a symbolic link
*/
bool symlink() const { return type == Node_type::SYMLINK; }
};
struct File_system::Control { /* to manipulate the executable bit */ };
/**
* Data structure returned when reading from a directory node
*/
struct File_system::Directory_entry
{
struct Name
{
char buf[MAX_NAME_LEN] { };
Name() { };
Name(char const *name) { Genode::strncpy(buf, name, sizeof(buf)); }
};
unsigned long inode;
Node_type type;
Node_rwx rwx;
Name name;
/**
* Sanitize object received from a file-system server as plain bytes
*/
void sanitize()
{
/* enforce null termination */
name.buf[MAX_NAME_LEN - 1] = 0;
}
};
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;
/**
* \noapi
*/
static const char *service_name() { return "File_system"; }
enum { CAP_QUOTA = 12 };
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
* \throw Unavailable directory vanished
*/
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
* \throw Unavailable directory vanished
*/
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;
/**
* Watch a node for for changes.
*
* When changes are made to the node at this path a CONTENT_CHANGED
* packet will be sent from the server to the client.
*
* \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
* \throw Unavailable file-system is static or does not support
* notifications
*
* The returned node handle is used to identify notification packets.
*/
virtual Watch_handle watch(Path const &path) = 0;
/**
* Close file
*
* \throw Invalid_handle node handle is invalid
*/
virtual void close(Node_handle) = 0;
/**
* Request information about an open file or directory
*
* \throw Invalid_handle node handle is invalid
* \throw Unavailable node vanished
*/
virtual Status status(Node_handle) = 0;
/**
* Set information about an open file or directory
*
* \throw Invalid_handle node handle is invalid
* \throw Unavailable node vanished
*/
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
* \throw Unavailable directory vanished
*/
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
* \throw Unavailable node vanished
*/
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
* \throw Unavailable a directory vanished
*/
virtual void move(Dir_handle, Name const &from,
Dir_handle, Name const &to) = 0;
/*******************
** 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, Unavailable),
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, Unavailable),
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_THROW(Rpc_watch, Watch_handle, watch,
GENODE_TYPE_LIST(Lookup_failed, Out_of_ram, Out_of_caps, Unavailable),
Path const &);
GENODE_RPC_THROW(Rpc_close, void, close,
GENODE_TYPE_LIST(Invalid_handle),
Node_handle);
GENODE_RPC_THROW(Rpc_status, Status, status,
GENODE_TYPE_LIST(Invalid_handle, Unavailable),
Node_handle);
GENODE_RPC_THROW(Rpc_control, void, control,
GENODE_TYPE_LIST(Invalid_handle, Unavailable),
Node_handle, Control);
GENODE_RPC_THROW(Rpc_unlink, void, unlink,
GENODE_TYPE_LIST(Invalid_handle, Invalid_name,
Lookup_failed, Not_empty,
Permission_denied, Unavailable),
Dir_handle, Name const &);
GENODE_RPC_THROW(Rpc_truncate, void, truncate,
GENODE_TYPE_LIST(Invalid_handle, No_space,
Permission_denied, Unavailable),
File_handle, file_size_t);
GENODE_RPC_THROW(Rpc_move, void, move,
GENODE_TYPE_LIST(Invalid_handle, Invalid_name,
Lookup_failed, Permission_denied, Unavailable),
Dir_handle, Name const &, Dir_handle, Name const &);
GENODE_RPC_INTERFACE(Rpc_tx_cap,
Rpc_file, Rpc_symlink, Rpc_dir,
Rpc_node, Rpc_watch,
Rpc_close, Rpc_status, Rpc_control, Rpc_unlink,
Rpc_truncate, Rpc_move);
};
#endif /* _INCLUDE__FILE_SYSTEM_SESSION__FILE_SYSTEM_SESSION_H_ */