/* * \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 #include #include #include #include namespace File_system { struct Node { typedef Genode::Id_space::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 Name; typedef Genode::Rpc_in_buffer 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 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::copy_cstring(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 Tx_policy; typedef Packet_stream_tx::Channel 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_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_ */