genode/repos/os/src/server/vfs/main.cc

899 lines
23 KiB
C++
Raw Normal View History

/*
* \brief VFS File_system server
* \author Emery Hemingway
* \author Christian Helmuth
* \date 2015-08-16
*/
/*
* Copyright (C) 2015-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.
*/
/* Genode includes */
#include <base/component.h>
#include <base/registry.h>
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
#include <file_system_session/rpc_object.h>
#include <root/component.h>
#include <os/session_policy.h>
#include <base/allocator_guard.h>
#include <vfs/dir_file_system.h>
#include <vfs/file_system_factory.h>
/* Local includes */
#include "assert.h"
#include "node.h"
namespace Vfs_server {
using namespace File_system;
using namespace Vfs;
class Session_component;
class Root;
class Event_response_handler;
typedef Genode::Registered<Session_component> Registered_session;
typedef Genode::Registry<Registered_session> Session_registry;
/**
* Convenience utities for parsing quotas
*/
Genode::Ram_quota parse_ram_quota(char const *args) {
return Genode::Ram_quota{
Genode::Arg_string::find_arg(args, "ram_quota").ulong_value(0)}; }
Genode::Cap_quota parse_cap_quota(char const *args) {
return Genode::Cap_quota{
Genode::Arg_string::find_arg(args, "cap_quota").ulong_value(0)}; }
};
class Vfs_server::Session_component : public File_system::Session_rpc_object,
public Session_io_handler
{
private:
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Node_space _node_space { };
Genode::Ram_quota_guard _ram_guard;
Genode::Cap_quota_guard _cap_guard;
Genode::Constrained_ram_allocator _ram_alloc;
Genode::Heap _alloc;
Genode::Signal_handler<Session_component> _process_packet_handler;
Vfs::Dir_file_system &_vfs;
/*
* The root node needs be allocated with the session struct
* but removeable from the id space at session destruction.
*/
Path const _root_path;
Genode::Session_label const _label;
bool const _writable;
/*
* XXX Currently, we have only one packet in backlog, which must finish
* processing before new packets can be processed.
*/
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Packet_descriptor _backlog_packet { };
/****************************
** Handle to node mapping **
****************************/
/**
* Apply functor to node
*
* \throw Invalid_handle
*/
template <typename FUNC>
void _apply_node(Node_handle handle, FUNC const &fn)
{
Node_space::Id id { handle.value };
try { return _node_space.apply<Node>(id, fn); }
catch (Node_space::Unknown_id) { throw Invalid_handle(); }
}
/**
* Apply functor to typed node
*
* \throw Invalid_handle
*/
template <typename HANDLE_TYPE, typename FUNC>
auto _apply(HANDLE_TYPE handle, FUNC const &fn)
-> typename Genode::Trait::Functor<decltype(&FUNC::operator())>::Return_type
{
Node_space::Id id { handle.value };
try { return _node_space.apply<Node>(id, [&] (Node &node) {
typedef typename Node_type<HANDLE_TYPE>::Type Typed_node;
Typed_node *n = dynamic_cast<Typed_node *>(&node);
if (!n)
throw Invalid_handle();
return fn(*n);
}); } catch (Node_space::Unknown_id) { throw Invalid_handle(); }
}
/******************************
** Packet-stream processing **
******************************/
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
struct Not_ready { };
struct Dont_ack { };
/**
* Perform packet operation
*
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
* \throw Not_ready
* \throw Dont_ack
*/
void _process_packet_op(Packet_descriptor &packet)
{
void * const content = tx_sink()->packet_content(packet);
size_t const length = packet.length();
seek_off_t const seek = packet.position();
/* assume failure by default */
packet.succeeded(false);
if ((packet.length() > packet.size()))
return;
/* resulting length */
size_t res_length = 0;
bool succeeded = false;
switch (packet.operation()) {
case Packet_descriptor::READ:
try {
_apply(packet.handle(), [&] (Io_node &node) {
if (!node.read_ready()) {
node.notify_read_ready(true);
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
throw Not_ready();
}
if (node.mode() & READ_ONLY) {
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
res_length = node.read((char *)content, length, seek);
/* no way to distinguish EOF from unsuccessful
reads, both have res_length == 0 */
succeeded = true;
}
});
}
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
catch (Not_ready) { throw; }
catch (Operation_incomplete) { throw Not_ready(); }
catch (...) { }
break;
case Packet_descriptor::WRITE:
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
try {
_apply(packet.handle(), [&] (Io_node &node) {
if (node.mode() & WRITE_ONLY) {
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
res_length = node.write((char const *)content, length, seek);
/* File system session can't handle partial writes */
if (res_length != length) {
Genode::error("partial write detected ",
res_length, " vs ", length);
/* don't acknowledge */
throw Dont_ack();
}
succeeded = true;
}
});
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
} catch (Operation_incomplete) {
throw Not_ready();
} catch (...) { }
break;
case Packet_descriptor::READ_READY:
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
try {
_apply(static_cast<File_handle>(packet.handle().value), [] (File &node) {
if (!node.read_ready()) {
node.notify_read_ready(true);
throw Dont_ack();
}
});
succeeded = true;
}
catch (Dont_ack) { throw; }
catch (...) { }
break;
case Packet_descriptor::CONTENT_CHANGED:
Genode::warning("ignoring CONTENT_CHANGED packet from client");
throw Dont_ack();
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
case Packet_descriptor::SYNC:
/**
* Sync the VFS and send any pending signals on the node.
*/
try {
_apply(packet.handle(), [&] (Io_node &node) {
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
node.sync();
succeeded = true;
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
});
} catch (Operation_incomplete) {
throw Not_ready();
} catch (...) { Genode::error("SYNC: unhandled exception"); }
break;
}
packet.length(res_length);
packet.succeeded(succeeded);
}
bool _try_process_packet_op(Packet_descriptor &packet)
{
try {
_process_packet_op(packet);
return true;
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
} catch (Not_ready) {
_backlog_packet = packet;
}
return false;
}
bool _process_backlog()
{
/* indicate success if there's no backlog */
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
if (!_backlog_packet.size() &&
(_backlog_packet.operation() != Packet_descriptor::SYNC)) {
return true;
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
}
/* only start processing if acknowledgement is possible */
if (!tx_sink()->ready_to_ack())
return false;
if (!_try_process_packet_op(_backlog_packet))
return false;
/*
* The 'acknowledge_packet' function cannot block because we
* checked for 'ready_to_ack' in '_process_packets'.
*/
tx_sink()->acknowledge_packet(_backlog_packet);
/* invalidate backlog packet */
_backlog_packet = Packet_descriptor();
return true;
}
bool _process_packet()
{
Packet_descriptor packet = tx_sink()->get_packet();
if (!_try_process_packet_op(packet))
return false;
/*
* The 'acknowledge_packet' function cannot block because we
* checked for 'ready_to_ack' in '_process_packets'.
*/
tx_sink()->acknowledge_packet(packet);
return true;
}
/**
* Called by signal dispatcher, executed in the context of the main
* thread (not serialized with the RPC functions)
*/
void _process_packets()
{
/*
* XXX Process client backlog before looking at new requests. This
* limits the number of simultaneously addressed handles (which
* was also the case before adding the backlog in case of
* blocking operations).
*/
if (!_process_backlog())
/* backlog not cleared - block for next condition change */
return;
while (tx_sink()->packet_avail()) {
/*
* Make sure that the '_process_packet' function does not
* block.
*
* If the acknowledgement queue is full, we defer packet
* processing until the client processed pending
* acknowledgements and thereby emitted a ready-to-ack
* signal. Otherwise, the call of 'acknowledge_packet()'
* in '_process_packet' would infinitely block the context
* of the main thread. The main thread is however needed
* for receiving any subsequent 'ready-to-ack' signals.
*/
if (!tx_sink()->ready_to_ack())
return;
try {
if (!_process_packet())
return;
} catch (Dont_ack) { }
}
}
/**
* Check if string represents a valid path (must start with '/')
*/
static void _assert_valid_path(char const *path) {
if (!path || path[0] != '/') throw Lookup_failed(); }
/**
* Check if string represents a valid name (must not contain '/')
*/
static void _assert_valid_name(char const *name)
{
if (!*name) throw Invalid_name();
for (char const *p = name; *p; ++p)
if (*p == '/')
throw Invalid_name();
}
void _close(Node &node)
{
if (File *file = dynamic_cast<File*>(&node))
destroy(_alloc, file);
else if (Directory *dir = dynamic_cast<Directory*>(&node))
destroy(_alloc, dir);
else if (Symlink *link = dynamic_cast<Symlink*>(&node))
destroy(_alloc, link);
else if (Watch_node *watch = dynamic_cast<Watch_node*>(&node))
destroy(_alloc, watch);
else
destroy(_alloc, &node);
}
public:
/**
* Constructor
* \param ep thead entrypoint for session
* \param cache node cache
* \param tx_buf_size shared transmission buffer size
* \param root_path path root of the session
* \param writable whether the session can modify files
*/
Session_component(Genode::Env &env,
char const *label,
Genode::Ram_quota ram_quota,
Genode::Cap_quota cap_quota,
size_t tx_buf_size,
Vfs::Dir_file_system &vfs,
char const *root_path,
bool writable)
:
Session_rpc_object(env.ram().alloc(tx_buf_size), env.rm(), env.ep().rpc_ep()),
_ram_guard(ram_quota),
_cap_guard(cap_quota),
_ram_alloc(env.pd(), _ram_guard, _cap_guard),
_alloc(_ram_alloc, env.rm()),
_process_packet_handler(env.ep(), *this, &Session_component::_process_packets),
_vfs(vfs),
_root_path(root_path),
_label(label),
_writable(writable)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
_tx.sigh_packet_avail(_process_packet_handler);
_tx.sigh_ready_to_ack(_process_packet_handler);
}
/**
* Destructor
*/
~Session_component()
{
while (_node_space.apply_any<Node>([&] (Node &node) {
_close(node); })) { }
}
/**
* Increase quotas
*/
void upgrade(Genode::Ram_quota ram) {
_ram_guard.upgrade(ram); }
void upgrade(Genode::Cap_quota caps) {
_cap_guard.upgrade(caps); }
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
/*
* Called by the IO response handler for events which are not
* node-specific, for example after 'release_packet()' to signal
* that a previously failed 'alloc_packet()' may succeed now.
*/
void handle_general_io() {
_process_packets(); }
/********************************
** Node_io_handler interface **
********************************/
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
void handle_node_io(Io_node &node) override
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
{
if (!tx_sink()->ready_to_ack())
Genode::error(
"dropping I/O notfication, congested packet buffer to '", _label, "'");
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
if (node.notify_read_ready() && node.read_ready()
&& tx_sink()->ready_to_ack()) {
Packet_descriptor packet(Packet_descriptor(),
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
Node_handle { node.id().value },
Packet_descriptor::READ_READY,
0, 0);
packet.succeeded(true);
tx_sink()->acknowledge_packet(packet);
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
node.notify_read_ready(false);
}
_process_packets();
}
void handle_node_watch(Watch_node &node) override
{
if (!tx_sink()->ready_to_ack())
Genode::error(
"dropping watch notfication, congested packet buffer to '", _label, "'");
if (tx_sink()->ready_to_ack()) {
Packet_descriptor packet(Packet_descriptor(),
Node_handle { node.id().value },
Packet_descriptor::CONTENT_CHANGED,
0, 0);
tx_sink()->acknowledge_packet(packet);
}
_process_packets();
}
/***************************
** File_system interface **
***************************/
Dir_handle dir(File_system::Path const &path, bool create) override
{
if (create && (!_writable))
throw Permission_denied();
char const *path_str = path.string();
if (!strcmp(path_str, "/") && create)
throw Node_already_exists();
_assert_valid_path(path_str);
Vfs_server::Path fullpath(_root_path);
if (path_str[1] != '\0')
fullpath.append(path_str);
path_str = fullpath.base();
if (!create && !_vfs.directory(path_str))
throw Lookup_failed();
Directory *dir;
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
try { dir = new (_alloc) Directory(_node_space, _vfs, _alloc,
*this, path_str, create); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Dir_handle(dir->id().value);
}
File_handle file(Dir_handle dir_handle, Name const &name,
Mode fs_mode, bool create) override
{
if ((create || (fs_mode & WRITE_ONLY)) && (!_writable))
throw Permission_denied();
return _apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
return File_handle {
dir.file(_node_space, _vfs, _alloc, name_str, fs_mode, create).value
};
});
}
Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create) override
{
if (create && !_writable) throw Permission_denied();
return _apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
return Symlink_handle {dir.symlink(
_node_space, _vfs, _alloc, name_str,
_writable ? READ_WRITE : READ_ONLY, create).value
};
});
}
Node_handle node(File_system::Path const &path) override
{
char const *path_str = path.string();
_assert_valid_path(path_str);
/* re-root the path */
Path sub_path(path_str+1, _root_path.base());
path_str = sub_path.base();
if (!_vfs.leaf_path(path_str))
throw Lookup_failed();
Node *node;
try { node = new (_alloc) Node(_node_space, path_str, *this); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Node_handle { node->id().value };
}
Watch_handle watch(File_system::Path const &path) override
{
char const *path_str = path.string();
_assert_valid_path(path_str);
/* re-root the path */
Path sub_path(path_str+1, _root_path.base());
path_str = sub_path.base();
Vfs::Vfs_watch_handle *vfs_handle = nullptr;
typedef Directory_service::Watch_result Result;
switch (_vfs.watch(path_str, &vfs_handle, _alloc)) {
case Result::WATCH_OK: break;
case Result::WATCH_ERR_UNACCESSIBLE:
throw Lookup_failed();
case Result::WATCH_ERR_STATIC:
throw Unavailable();
case Result::WATCH_ERR_OUT_OF_RAM:
throw Out_of_ram();
case Result::WATCH_ERR_OUT_OF_CAPS:
throw Out_of_caps();
}
Node *node;
try { node = new (_alloc)
Watch_node(_node_space, path_str, *vfs_handle, *this); }
catch (Out_of_memory) { throw Out_of_ram(); }
return Watch_handle { node->id().value };
}
void close(Node_handle handle) override
{
try { _apply_node(handle, [&] (Node &node) {
_close(node);
}); } catch (File_system::Invalid_handle) { }
}
Status status(Node_handle node_handle) override
{
File_system::Status fs_stat;
_apply_node(node_handle, [&] (Node &node) {
Directory_service::Stat vfs_stat;
if (_vfs.stat(node.path(), vfs_stat) != Directory_service::STAT_OK)
throw Invalid_handle();
fs_stat.inode = vfs_stat.inode;
switch (vfs_stat.mode & (
Directory_service::STAT_MODE_DIRECTORY |
Directory_service::STAT_MODE_SYMLINK |
File_system::Status::MODE_FILE)) {
case Directory_service::STAT_MODE_DIRECTORY:
fs_stat.mode = File_system::Status::MODE_DIRECTORY;
fs_stat.size = _vfs.num_dirent(node.path()) * sizeof(Directory_entry);
return;
case Directory_service::STAT_MODE_SYMLINK:
fs_stat.mode = File_system::Status::MODE_SYMLINK;
break;
default: /* Directory_service::STAT_MODE_FILE */
fs_stat.mode = File_system::Status::MODE_FILE;
break;
}
fs_stat.size = vfs_stat.size;
});
return fs_stat;
}
void unlink(Dir_handle dir_handle, Name const &name) override
{
if (!_writable) throw Permission_denied();
_apply(dir_handle, [&] (Directory &dir) {
char const *name_str = name.string();
_assert_valid_name(name_str);
Path path(name_str, dir.path());
assert_unlink(_vfs.unlink(path.base()));
dir.mark_as_updated();
});
}
void truncate(File_handle file_handle, file_size_t size) override
{
_apply(file_handle, [&] (File &file) {
file.truncate(size); });
}
void move(Dir_handle from_dir_handle, Name const &from_name,
Dir_handle to_dir_handle, Name const &to_name) override
{
if (!_writable)
throw Permission_denied();
char const *from_str = from_name.string();
char const *to_str = to_name.string();
_assert_valid_name(from_str);
_assert_valid_name( to_str);
_apply(from_dir_handle, [&] (Directory &from_dir) {
_apply(to_dir_handle, [&] (Directory &to_dir) {
Path from_path(from_str, from_dir.path());
Path to_path( to_str, to_dir.path());
assert_rename(_vfs.rename(from_path.base(), to_path.base()));
from_dir.mark_as_updated();
to_dir.mark_as_updated();
});
});
}
void control(Node_handle, Control) override { }
};
/**
* Global vfs event handler
*/
struct Vfs_server::Event_response_handler : Vfs::Io_response_handler
{
Session_registry &_session_registry;
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
bool _in_progress { false };
bool _handle_general_io { false };
Event_response_handler(Session_registry &session_registry)
: _session_registry(session_registry) { }
void handle_io_response(Vfs::Vfs_handle::Context *context) override
{
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
if (_in_progress) {
/* called recursively, context is nullptr in this case */
_handle_general_io = true;
return;
}
_in_progress = true;
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
if (context)
Io_node::node_by_context(*context).handle_io_response();
VFS: nonblocking interface The VFS library can be used in single-threaded or multi-threaded environments and depending on that, signals are handled by the same thread which uses the VFS library or possibly by a different thread. If a VFS plugin needs to block to wait for a signal, there is currently no way which works reliably in both environments. For this reason, this commit makes the interface of the VFS library nonblocking, similar to the File_system session interface. The most important changes are: - Directories are created and opened with the 'opendir()' function and the directory entries are read with the recently introduced 'queue_read()' and 'complete_read()' functions. - Symbolic links are created and opened with the 'openlink()' function and the link target is read with the 'queue_read()' and 'complete_read()' functions and written with the 'write()' function. - The 'write()' function does not wait for signals anymore. This can have the effect that data written by a VFS library user has not been processed by a file system server yet when the library user asks for the size of the file or closes it (both done with RPC functions at the file system server). For this reason, a user of the VFS library should request synchronization before calling 'stat()' or 'close()'. To make sure that a file system server has processed all write request packets which a client submitted before the synchronization request, synchronization is now requested at the file system server with a synchronization packet instead of an RPC function. Because of this change, the synchronization interface of the VFS library is now split into 'queue_sync()' and 'complete_sync()' functions. Fixes #2399
2017-08-15 20:51:53 +02:00
else
_handle_general_io = true;
while (_handle_general_io) {
_handle_general_io = false;
_session_registry.for_each([ ] (Registered_session &r) {
r.handle_general_io();
});
}
_in_progress = false;
}
void handle_watch_response(Vfs::Vfs_watch_handle::Context *context) override
{
if (context)
Watch_node::node_by_context(*context).handle_watch_response();
}
};
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
class Vfs_server::Root : public Genode::Root_component<Session_component>
{
private:
Genode::Env &_env;
/* heap for internal VFS allocation */
Genode::Heap _vfs_heap { &_env.ram(), &_env.rm() };
Genode::Attached_rom_dataspace _config_rom { _env, "config" };
Genode::Xml_node vfs_config()
{
try { return _config_rom.xml().sub_node("vfs"); }
catch (...) {
Genode::error("VFS not configured");
_env.parent().exit(~0);
throw;
}
}
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Session_registry _session_registry { };
Event_response_handler _response_handler { _session_registry };
Vfs::Global_file_system_factory _global_file_system_factory { _vfs_heap };
Vfs::Dir_file_system _vfs {
_env, _vfs_heap, vfs_config(), _response_handler,
_global_file_system_factory };
Genode::Signal_handler<Root> _config_handler {
2017-02-07 19:03:23 +01:00
_env.ep(), *this, &Root::_config_update };
void _config_update()
{
_config_rom.update();
_vfs.apply_config(vfs_config());
}
protected:
Session_component *_create_session(const char *args) override
{
using namespace Genode;
Session_label const label = label_from_args(args);
Path session_root;
bool writeable = false;
/*****************
** Quota check **
*****************/
auto const initial_ram_usage = _env.pd().used_ram().value;
auto const initial_cap_usage = _env.pd().used_caps().value;
auto const ram_quota = parse_ram_quota(args).value;
auto const cap_quota = parse_cap_quota(args).value;
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").aligned_size();
if (!tx_buf_size)
throw Service_denied();
size_t session_size =
max((size_t)4096, sizeof(Session_component)) +
tx_buf_size;
if (session_size > ram_quota) {
error("insufficient 'ram_quota' from '", label, "' "
"got ", ram_quota, ", need ", session_size);
throw Insufficient_ram_quota();
}
/**************************
** Apply session policy **
**************************/
/* pull in policy changes */
_config_rom.update();
char tmp[MAX_PATH_LEN];
try {
Session_policy policy(label, _config_rom.xml());
/* determine optional session root offset. */
try {
policy.attribute("root").value(tmp, sizeof(tmp));
session_root.import(tmp, "/");
} catch (Xml_node::Nonexistent_attribute) { }
/*
* Determine if the session is writeable.
* Policy overrides client argument, both default to false.
*/
if (policy.attribute_value("writeable", false))
writeable = Arg_string::find_arg(args, "writeable").bool_value(false);
} catch (Session_policy::No_policy_defined) {
/* missing policy - deny request */
throw Service_denied();
}
/* apply client's root offset. */
Arg_string::find_arg(args, "root").string(tmp, sizeof(tmp), "/");
if (Genode::strcmp("/", tmp, sizeof(tmp))) {
session_root.append("/");
session_root.append(tmp);
session_root.remove_trailing('/');
}
/* check if the session root exists */
if (!((session_root == "/") || _vfs.directory(session_root.base()))) {
error("session root '", session_root, "' not found for '", label, "'");
throw Service_denied();
}
Session_component *session = new (md_alloc())
Registered_session(_session_registry, _env, label.string(),
Genode::Ram_quota{ram_quota},
Genode::Cap_quota{cap_quota},
tx_buf_size, _vfs,
session_root.base(), writeable);
auto ram_used = _env.pd().used_ram().value - initial_ram_usage;
auto cap_used = _env.pd().used_caps().value - initial_cap_usage;
if ((ram_used > ram_quota) || (cap_used > cap_quota)) {
if (ram_used > ram_quota)
Genode::warning("ram donation is ", ram_quota,
" but used RAM is ", ram_used, "B"
", '", label, "'");
if (cap_used > cap_quota)
Genode::warning("cap donation is ", cap_quota,
" but used caps is ", cap_used,
", '", label, "'");
}
Genode::log("session opened for '", label, "' at '", session_root, "'");
return session;
}
void _upgrade_session(Session_component *session,
char const *args) override
{
Genode::Ram_quota more_ram = parse_ram_quota(args);
Genode::Cap_quota more_caps = parse_cap_quota(args);
if (more_ram.value > 0)
session->upgrade(more_ram);
if (more_caps.value > 0)
session->upgrade(more_caps);
}
public:
Root(Genode::Env &env, Genode::Allocator &md_alloc)
:
Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
_env(env)
{
_config_rom.sigh(_config_handler);
env.parent().announce(env.ep().manage(*this));
}
};
void Component::construct(Genode::Env &env)
{
static Genode::Sliced_heap sliced_heap { &env.ram(), &env.rm() };
static Vfs_server::Root root { env, sliced_heap };
}