From 547cc06976cfcbe856cdf8d9d087e193a520d0eb Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Tue, 26 Sep 2017 16:35:17 +0200 Subject: [PATCH] ram_fs: throw exception when unlinked node gets accessed Fixes #2536 --- repos/dde_rump/src/server/rump_fs/main.cc | 2 +- repos/dde_rump/src/server/rump_fs/open_node.h | 95 +++++++++++++++ repos/libports/src/server/fatfs_fs/main.cc | 2 +- .../libports/src/server/fatfs_fs/open_node.h | 95 +++++++++++++++ .../src/server/fuse_fs/fuse_fs_main.cc | 2 +- repos/libports/src/server/fuse_fs/open_node.h | 95 +++++++++++++++ repos/os/include/file_system/open_node.h | 40 ++++-- .../file_system_session/file_system_session.h | 22 ++-- repos/os/src/lib/vfs/fs_file_system.h | 4 + repos/os/src/server/lx_fs/main.cc | 3 +- repos/os/src/server/lx_fs/open_node.h | 95 +++++++++++++++ repos/os/src/server/ram_fs/main.cc | 115 ++++++++++++------ repos/os/src/server/ram_fs/node.h | 5 +- repos/os/src/server/trace_fs/main.cc | 62 +++++++--- repos/os/src/server/trace_fs/node.h | 5 +- 15 files changed, 561 insertions(+), 81 deletions(-) create mode 100644 repos/dde_rump/src/server/rump_fs/open_node.h create mode 100644 repos/libports/src/server/fatfs_fs/open_node.h create mode 100644 repos/libports/src/server/fuse_fs/open_node.h create mode 100644 repos/os/src/server/lx_fs/open_node.h diff --git a/repos/dde_rump/src/server/rump_fs/main.cc b/repos/dde_rump/src/server/rump_fs/main.cc index afdbd46347..534e9c2e30 100644 --- a/repos/dde_rump/src/server/rump_fs/main.cc +++ b/repos/dde_rump/src/server/rump_fs/main.cc @@ -13,7 +13,6 @@ */ /* Genode includes */ -#include #include #include #include @@ -29,6 +28,7 @@ #include #include "file_system.h" #include "directory.h" +#include "open_node.h" namespace Rump_fs { diff --git a/repos/dde_rump/src/server/rump_fs/open_node.h b/repos/dde_rump/src/server/rump_fs/open_node.h new file mode 100644 index 0000000000..942976828b --- /dev/null +++ b/repos/dde_rump/src/server/rump_fs/open_node.h @@ -0,0 +1,95 @@ +/* + * \brief Representation of an open file system node within the component (deprecated) + * \author Christian Prochaska + * \date 2017-06-09 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _OPEN_NODE_H_ +#define _OPEN_NODE_H_ + +/* Genode includes */ +#include +#include + +namespace File_system { + /* + * \param NODE component-specific node type + */ + template class Open_node; +} + +template +class File_system::Open_node : public File_system::Node +{ + private: + + Genode::Id_space::Element _element; + + NODE &_node; + Genode::Constructible _listener; + + Listener::Version const _version_when_opened = _node.curr_version(); + + /* + * Flag to track whether the underlying file-system node was + * modified via this 'Open_node'. That is, if closing the 'Open_node' + * should notify listeners of the file. + */ + bool _was_written = false; + + public: + + Open_node(NODE &node, Genode::Id_space &id_space) + : _element(*this, id_space), _node(node) { } + + ~Open_node() + { + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Notify remaining listeners about the changed file + */ + if (_was_written) + _node.notify_listeners(); + } + + NODE &node() { return _node; } + File_system::Listener &listener() { return *_listener; } + + Genode::Id_space::Id id() { return _element.id(); } + + /** + * Register packet stream sink to be notified of node changes + */ + void register_notify(File_system::Sink &sink) + { + /* + * If there was already a handler registered for the node, + * remove the old handler. + */ + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Register new handler + */ + _listener.construct(sink, id(), _version_when_opened); + _node.add_listener(&*_listener); + } + + void mark_as_written() { _was_written = true; } +}; + +#endif /* _OPEN_NODE_H_ */ diff --git a/repos/libports/src/server/fatfs_fs/main.cc b/repos/libports/src/server/fatfs_fs/main.cc index becee163a7..d47c921e77 100644 --- a/repos/libports/src/server/fatfs_fs/main.cc +++ b/repos/libports/src/server/fatfs_fs/main.cc @@ -13,7 +13,6 @@ /* Genode includes */ #include -#include #include #include #include @@ -25,6 +24,7 @@ /* local includes */ #include #include +#include #include /* Genode block backend */ diff --git a/repos/libports/src/server/fatfs_fs/open_node.h b/repos/libports/src/server/fatfs_fs/open_node.h new file mode 100644 index 0000000000..942976828b --- /dev/null +++ b/repos/libports/src/server/fatfs_fs/open_node.h @@ -0,0 +1,95 @@ +/* + * \brief Representation of an open file system node within the component (deprecated) + * \author Christian Prochaska + * \date 2017-06-09 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _OPEN_NODE_H_ +#define _OPEN_NODE_H_ + +/* Genode includes */ +#include +#include + +namespace File_system { + /* + * \param NODE component-specific node type + */ + template class Open_node; +} + +template +class File_system::Open_node : public File_system::Node +{ + private: + + Genode::Id_space::Element _element; + + NODE &_node; + Genode::Constructible _listener; + + Listener::Version const _version_when_opened = _node.curr_version(); + + /* + * Flag to track whether the underlying file-system node was + * modified via this 'Open_node'. That is, if closing the 'Open_node' + * should notify listeners of the file. + */ + bool _was_written = false; + + public: + + Open_node(NODE &node, Genode::Id_space &id_space) + : _element(*this, id_space), _node(node) { } + + ~Open_node() + { + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Notify remaining listeners about the changed file + */ + if (_was_written) + _node.notify_listeners(); + } + + NODE &node() { return _node; } + File_system::Listener &listener() { return *_listener; } + + Genode::Id_space::Id id() { return _element.id(); } + + /** + * Register packet stream sink to be notified of node changes + */ + void register_notify(File_system::Sink &sink) + { + /* + * If there was already a handler registered for the node, + * remove the old handler. + */ + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Register new handler + */ + _listener.construct(sink, id(), _version_when_opened); + _node.add_listener(&*_listener); + } + + void mark_as_written() { _was_written = true; } +}; + +#endif /* _OPEN_NODE_H_ */ diff --git a/repos/libports/src/server/fuse_fs/fuse_fs_main.cc b/repos/libports/src/server/fuse_fs/fuse_fs_main.cc index 91c8889f4f..36d893025e 100644 --- a/repos/libports/src/server/fuse_fs/fuse_fs_main.cc +++ b/repos/libports/src/server/fuse_fs/fuse_fs_main.cc @@ -13,7 +13,6 @@ /* Genode includes */ #include -#include #include #include #include @@ -26,6 +25,7 @@ /* local includes */ #include +#include #include diff --git a/repos/libports/src/server/fuse_fs/open_node.h b/repos/libports/src/server/fuse_fs/open_node.h new file mode 100644 index 0000000000..942976828b --- /dev/null +++ b/repos/libports/src/server/fuse_fs/open_node.h @@ -0,0 +1,95 @@ +/* + * \brief Representation of an open file system node within the component (deprecated) + * \author Christian Prochaska + * \date 2017-06-09 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _OPEN_NODE_H_ +#define _OPEN_NODE_H_ + +/* Genode includes */ +#include +#include + +namespace File_system { + /* + * \param NODE component-specific node type + */ + template class Open_node; +} + +template +class File_system::Open_node : public File_system::Node +{ + private: + + Genode::Id_space::Element _element; + + NODE &_node; + Genode::Constructible _listener; + + Listener::Version const _version_when_opened = _node.curr_version(); + + /* + * Flag to track whether the underlying file-system node was + * modified via this 'Open_node'. That is, if closing the 'Open_node' + * should notify listeners of the file. + */ + bool _was_written = false; + + public: + + Open_node(NODE &node, Genode::Id_space &id_space) + : _element(*this, id_space), _node(node) { } + + ~Open_node() + { + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Notify remaining listeners about the changed file + */ + if (_was_written) + _node.notify_listeners(); + } + + NODE &node() { return _node; } + File_system::Listener &listener() { return *_listener; } + + Genode::Id_space::Id id() { return _element.id(); } + + /** + * Register packet stream sink to be notified of node changes + */ + void register_notify(File_system::Sink &sink) + { + /* + * If there was already a handler registered for the node, + * remove the old handler. + */ + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Register new handler + */ + _listener.construct(sink, id(), _version_when_opened); + _node.add_listener(&*_listener); + } + + void mark_as_written() { _was_written = true; } +}; + +#endif /* _OPEN_NODE_H_ */ diff --git a/repos/os/include/file_system/open_node.h b/repos/os/include/file_system/open_node.h index 4a16e3ace5..86a10952a4 100644 --- a/repos/os/include/file_system/open_node.h +++ b/repos/os/include/file_system/open_node.h @@ -32,10 +32,10 @@ class File_system::Open_node : public File_system::Node Genode::Id_space::Element _element; - NODE &_node; - Genode::Constructible _listener; + Genode::Weak_ptr _node; + Genode::Constructible _listener; - Listener::Version const _version_when_opened = _node.curr_version(); + Listener::Version const _version_when_opened { _node_version(_node) }; /* * Flag to track whether the underlying file-system node was @@ -44,15 +44,29 @@ class File_system::Open_node : public File_system::Node */ bool _was_written = false; + static Listener::Version const _node_version(Genode::Weak_ptr node) + { + Genode::Locked_ptr locked_node { node }; + + if (locked_node.valid()) + return locked_node->curr_version(); + else + return Listener::Version { 0 }; + } + public: - Open_node(NODE &node, Genode::Id_space &id_space) + Open_node(Genode::Weak_ptr node, + Genode::Id_space &id_space) : _element(*this, id_space), _node(node) { } ~Open_node() { + Genode::Locked_ptr node { _node }; + if (_listener.constructed()) { - _node.remove_listener(&*_listener); + if (node.valid()) + node->remove_listener(&*_listener); _listener.destruct(); } @@ -60,10 +74,11 @@ class File_system::Open_node : public File_system::Node * Notify remaining listeners about the changed file */ if (_was_written) - _node.notify_listeners(); + if (node.valid()) + node->notify_listeners(); } - NODE &node() { return _node; } + Genode::Weak_ptr&node() { return _node; } File_system::Listener &listener() { return *_listener; } Genode::Id_space::Id id() { return _element.id(); } @@ -73,20 +88,25 @@ class File_system::Open_node : public File_system::Node */ void register_notify(File_system::Sink &sink) { + Genode::Locked_ptr node { _node }; + /* * If there was already a handler registered for the node, * remove the old handler. */ if (_listener.constructed()) { - _node.remove_listener(&*_listener); + if (node.valid()) + node->remove_listener(&*_listener); _listener.destruct(); } /* * Register new handler */ - _listener.construct(sink, id(), _version_when_opened); - _node.add_listener(&*_listener); + if (node.valid()) { + _listener.construct(sink, id(), _version_when_opened); + node->add_listener(&*_listener); + } } void mark_as_written() { _was_written = true; } diff --git a/repos/os/include/file_system_session/file_system_session.h b/repos/os/include/file_system_session/file_system_session.h index e6ec64df78..0bf0303254 100644 --- a/repos/os/include/file_system_session/file_system_session.h +++ b/repos/os/include/file_system_session/file_system_session.h @@ -105,6 +105,7 @@ namespace File_system { class No_space : Exception { }; class Not_empty : Exception { }; class Permission_denied : Exception { }; + class Unavailable : Exception { }; struct Session; } @@ -288,6 +289,7 @@ struct File_system::Session : public Genode::Session * \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; @@ -303,6 +305,7 @@ struct File_system::Session : public Genode::Session * \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; @@ -345,6 +348,7 @@ struct File_system::Session : public Genode::Session * 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; @@ -352,6 +356,7 @@ struct File_system::Session : public Genode::Session * 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; @@ -364,6 +369,7 @@ struct File_system::Session : public Genode::Session * \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; @@ -373,6 +379,7 @@ struct File_system::Session : public Genode::Session * \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; @@ -383,6 +390,7 @@ struct File_system::Session : public Genode::Session * \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; @@ -397,13 +405,13 @@ struct File_system::Session : public Genode::Session GENODE_TYPE_LIST(Invalid_handle, Invalid_name, Lookup_failed, Node_already_exists, No_space, Out_of_ram, Out_of_caps, - Permission_denied), + 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), + Permission_denied, Unavailable), Dir_handle, Name const &, bool); GENODE_RPC_THROW(Rpc_dir, Dir_handle, dir, GENODE_TYPE_LIST(Lookup_failed, Name_too_long, @@ -417,23 +425,23 @@ struct File_system::Session : public Genode::Session GENODE_TYPE_LIST(Invalid_handle), Node_handle); GENODE_RPC_THROW(Rpc_status, Status, status, - GENODE_TYPE_LIST(Invalid_handle), + GENODE_TYPE_LIST(Invalid_handle, Unavailable), Node_handle); GENODE_RPC_THROW(Rpc_control, void, control, - GENODE_TYPE_LIST(Invalid_handle), + 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), + Permission_denied, Unavailable), Dir_handle, Name const &); GENODE_RPC_THROW(Rpc_truncate, void, truncate, GENODE_TYPE_LIST(Invalid_handle, No_space, - Permission_denied), + 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), + 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, diff --git a/repos/os/src/lib/vfs/fs_file_system.h b/repos/os/src/lib/vfs/fs_file_system.h index 69b9474da1..2622c7c855 100644 --- a/repos/os/src/lib/vfs/fs_file_system.h +++ b/repos/os/src/lib/vfs/fs_file_system.h @@ -638,6 +638,7 @@ class Vfs::Fs_file_system : public File_system catch (::File_system::Lookup_failed) { return UNLINK_ERR_NO_ENTRY; } catch (::File_system::Not_empty) { return UNLINK_ERR_NOT_EMPTY; } catch (::File_system::Permission_denied) { return UNLINK_ERR_NO_PERM; } + catch (::File_system::Unavailable) { return UNLINK_ERR_NO_ENTRY; } return UNLINK_OK; } @@ -765,6 +766,7 @@ class Vfs::Fs_file_system : public File_system catch (::File_system::No_space) { return OPEN_ERR_NO_SPACE; } catch (::File_system::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } catch (::File_system::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + catch (::File_system::Unavailable) { return OPEN_ERR_UNACCESSIBLE; } return OPEN_OK; } @@ -834,6 +836,7 @@ class Vfs::Fs_file_system : public File_system catch (::File_system::Out_of_ram) { return OPENLINK_ERR_OUT_OF_RAM; } catch (::File_system::Out_of_caps) { return OPENLINK_ERR_OUT_OF_CAPS; } catch (::File_system::Permission_denied) { return OPENLINK_ERR_PERMISSION_DENIED; } + catch (::File_system::Unavailable) { return OPENLINK_ERR_LOOKUP_FAILED; } } void close(Vfs_handle *vfs_handle) override @@ -940,6 +943,7 @@ class Vfs::Fs_file_system : public File_system catch (::File_system::Invalid_handle) { return FTRUNCATE_ERR_NO_PERM; } catch (::File_system::Permission_denied) { return FTRUNCATE_ERR_NO_PERM; } catch (::File_system::No_space) { return FTRUNCATE_ERR_NO_SPACE; } + catch (::File_system::Unavailable) { return FTRUNCATE_ERR_NO_PERM; } return FTRUNCATE_OK; } diff --git a/repos/os/src/server/lx_fs/main.cc b/repos/os/src/server/lx_fs/main.cc index 62b31fa696..7676db2f41 100644 --- a/repos/os/src/server/lx_fs/main.cc +++ b/repos/os/src/server/lx_fs/main.cc @@ -16,14 +16,13 @@ #include #include #include -#include #include #include #include /* local includes */ #include - +#include namespace Lx_fs { diff --git a/repos/os/src/server/lx_fs/open_node.h b/repos/os/src/server/lx_fs/open_node.h new file mode 100644 index 0000000000..942976828b --- /dev/null +++ b/repos/os/src/server/lx_fs/open_node.h @@ -0,0 +1,95 @@ +/* + * \brief Representation of an open file system node within the component (deprecated) + * \author Christian Prochaska + * \date 2017-06-09 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _OPEN_NODE_H_ +#define _OPEN_NODE_H_ + +/* Genode includes */ +#include +#include + +namespace File_system { + /* + * \param NODE component-specific node type + */ + template class Open_node; +} + +template +class File_system::Open_node : public File_system::Node +{ + private: + + Genode::Id_space::Element _element; + + NODE &_node; + Genode::Constructible _listener; + + Listener::Version const _version_when_opened = _node.curr_version(); + + /* + * Flag to track whether the underlying file-system node was + * modified via this 'Open_node'. That is, if closing the 'Open_node' + * should notify listeners of the file. + */ + bool _was_written = false; + + public: + + Open_node(NODE &node, Genode::Id_space &id_space) + : _element(*this, id_space), _node(node) { } + + ~Open_node() + { + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Notify remaining listeners about the changed file + */ + if (_was_written) + _node.notify_listeners(); + } + + NODE &node() { return _node; } + File_system::Listener &listener() { return *_listener; } + + Genode::Id_space::Id id() { return _element.id(); } + + /** + * Register packet stream sink to be notified of node changes + */ + void register_notify(File_system::Sink &sink) + { + /* + * If there was already a handler registered for the node, + * remove the old handler. + */ + if (_listener.constructed()) { + _node.remove_listener(&*_listener); + _listener.destruct(); + } + + /* + * Register new handler + */ + _listener.construct(sink, id(), _version_when_opened); + _node.add_listener(&*_listener); + } + + void mark_as_written() { _was_written = true; } +}; + +#endif /* _OPEN_NODE_H_ */ diff --git a/repos/os/src/server/ram_fs/main.cc b/repos/os/src/server/ram_fs/main.cc index 5a3cf49b0e..64fa43be51 100644 --- a/repos/os/src/server/ram_fs/main.cc +++ b/repos/os/src/server/ram_fs/main.cc @@ -76,30 +76,47 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object switch (packet.operation()) { case Packet_descriptor::READ: - if (content && (packet.length() <= packet.size())) - res_length = open_node.node().read((char *)content, length, packet.position()); + if (content && (packet.length() <= packet.size())) { + Locked_ptr node { open_node.node() }; + if (!node.valid()) + break; + res_length = node->read((char *)content, length, + packet.position()); + } break; case Packet_descriptor::WRITE: - if (content && (packet.length() <= packet.size())) - res_length = open_node.node().write((char const *)content, length, packet.position()); - + if (content && (packet.length() <= packet.size())) { + Locked_ptr node { open_node.node() }; + if (!node.valid()) + break; + res_length = node->write((char const *)content, length, + packet.position()); + } open_node.mark_as_written(); break; - case Packet_descriptor::CONTENT_CHANGED: + case Packet_descriptor::CONTENT_CHANGED: { open_node.register_notify(*tx_sink()); - open_node.node().notify_listeners(); + Locked_ptr node { open_node.node() }; + if (!node.valid()) + return; + node->notify_listeners(); return; + } case Packet_descriptor::READ_READY: /* not supported */ break; - case Packet_descriptor::SYNC: - open_node.node().notify_listeners(); + case Packet_descriptor::SYNC: { + Locked_ptr node { open_node.node() }; + if (!node.valid()) + break; + node->notify_listeners(); break; } + } packet.length(res_length); packet.succeeded(res_length > 0); @@ -207,7 +224,10 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object auto file_fn = [&] (Open_node &open_node) { - Node &dir = open_node.node(); + Locked_ptr dir { open_node.node() }; + + if (!dir.valid()) + throw Unavailable(); if (!_writable) if (mode != STAT_ONLY && mode != READ_ONLY) @@ -218,23 +238,23 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object if (!_writable) throw Permission_denied(); - if (dir.has_sub_node_unsynchronized(name.string())) + if (dir->has_sub_node_unsynchronized(name.string())) throw Node_already_exists(); try { File * const file = new (_alloc) File(_alloc, name.string()); - dir.adopt_unsynchronized(file); + dir->adopt_unsynchronized(file); open_node.mark_as_written(); } catch (Allocator::Out_of_memory) { throw No_space(); } } - File *file = dir.lookup_file(name.string()); + File *file = dir->lookup_file(name.string()); Open_node *open_file = - new (_alloc) Open_node(*file, _open_node_registry); + new (_alloc) Open_node(file->weak_ptr(), _open_node_registry); return open_file->id(); }; @@ -255,29 +275,32 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object auto symlink_fn = [&] (Open_node &open_node) { - Node &dir = open_node.node(); + Locked_ptr dir { open_node.node() }; + + if (!dir.valid()) + throw Unavailable(); if (create) { if (!_writable) throw Permission_denied(); - if (dir.has_sub_node_unsynchronized(name.string())) + if (dir->has_sub_node_unsynchronized(name.string())) throw Node_already_exists(); try { Symlink * const symlink = new (_alloc) Symlink(name.string()); - dir.adopt_unsynchronized(symlink); + dir->adopt_unsynchronized(symlink); } catch (Allocator::Out_of_memory) { throw No_space(); } } - Symlink *symlink = dir.lookup_symlink(name.string()); + Symlink *symlink = dir->lookup_symlink(name.string()); Open_node *open_symlink = - new (_alloc) Open_node(*symlink, _open_node_registry); + new (_alloc) Open_node(symlink->weak_ptr(), _open_node_registry); return open_symlink->id(); }; @@ -325,7 +348,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object Directory *dir = _root.lookup_dir(path_str); Open_node *open_dir = - new (_alloc) Open_node(*dir, _open_node_registry); + new (_alloc) Open_node(dir->weak_ptr(), _open_node_registry); return Dir_handle { open_dir->id().value }; } @@ -337,7 +360,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object Node *node = _root.lookup(path.string() + 1); Open_node *open_node = - new (_alloc) Open_node(*node, _open_node_registry); + new (_alloc) Open_node(node->weak_ptr(), _open_node_registry); return open_node->id(); } @@ -345,7 +368,8 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object void close(Node_handle handle) { auto close_fn = [&] (Open_node &open_node) { - destroy(_alloc, &open_node); }; + destroy(_alloc, &open_node); + }; try { _open_node_registry.apply(handle, close_fn); @@ -357,7 +381,11 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object Status status(Node_handle node_handle) { auto status_fn = [&] (Open_node &open_node) { - return open_node.node().status(); }; + Locked_ptr node { open_node.node() }; + if (!node.valid()) + throw Unavailable(); + return node->status(); + }; try { return _open_node_registry.apply(node_handle, status_fn); @@ -378,14 +406,14 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object auto unlink_fn = [&] (Open_node &open_node) { - Node &dir = open_node.node(); + Locked_ptr dir { open_node.node() }; - Node *node = dir.lookup(name.string()); + if (!dir.valid()) + throw Unavailable(); - dir.discard(node); + Node *node = dir->lookup(name.string()); - // XXX implement ref counting, do not destroy node that is - // is still referenced by a node handle + dir->discard(node); destroy(_alloc, node); open_node.mark_as_written(); @@ -404,7 +432,10 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object throw Permission_denied(); auto truncate_fn = [&] (Open_node &open_node) { - open_node.node().truncate(size); + Locked_ptr node { open_node.node() }; + if (!node.valid()) + throw Unavailable(); + node->truncate(size); open_node.mark_as_written(); }; @@ -431,17 +462,23 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object auto inner_move_fn = [&] (Open_node &open_to_dir_node) { - Node &from_dir = open_from_dir_node.node(); + Locked_ptr from_dir { open_from_dir_node.node() }; - Node *node = from_dir.lookup(from_name.string()); + if (!from_dir.valid()) + throw Unavailable(); + + Node *node = from_dir->lookup(from_name.string()); node->name(to_name.string()); - Node &to_dir = open_to_dir_node.node(); + if (!(open_to_dir_node.node() == open_from_dir_node.node())) { - if (&to_dir != &from_dir) { + Locked_ptr to_dir { open_to_dir_node.node() }; - from_dir.discard(node); - to_dir.adopt_unsynchronized(node); + if (!to_dir.valid()) + throw Unavailable(); + + from_dir->discard(node); + to_dir->adopt_unsynchronized(node); /* * If the file was moved from one directory to another we @@ -449,13 +486,13 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object * directory 'from_dir' will always get notified (i.e., * when just the file name was changed) below. */ - to_dir.mark_as_updated(); + to_dir->mark_as_updated(); open_to_dir_node.mark_as_written(); - to_dir.notify_listeners(); + to_dir->notify_listeners(); - from_dir.mark_as_updated(); + from_dir->mark_as_updated(); open_from_dir_node.mark_as_written(); - from_dir.notify_listeners(); + from_dir->notify_listeners(); node->mark_as_updated(); node->notify_listeners(); diff --git a/repos/os/src/server/ram_fs/node.h b/repos/os/src/server/ram_fs/node.h index 4de3564864..fc34ad23b3 100644 --- a/repos/os/src/server/ram_fs/node.h +++ b/repos/os/src/server/ram_fs/node.h @@ -29,7 +29,8 @@ namespace Ram_fs { } -class Ram_fs::Node : public File_system::Node_base, public List::Element +class Ram_fs::Node : public File_system::Node_base, public Weak_object, + public List::Element { public: @@ -56,6 +57,8 @@ class Ram_fs::Node : public File_system::Node_base, public List::Element : _ref_count(0), _inode(_unique_inode()) { _name[0] = 0; } + virtual ~Node() { lock_for_destruction(); } + unsigned long inode() const { return _inode; } char const *name() const { return _name; } diff --git a/repos/os/src/server/trace_fs/main.cc b/repos/os/src/server/trace_fs/main.cc index 65a7cdae4b..ae41c3582c 100644 --- a/repos/os/src/server/trace_fs/main.cc +++ b/repos/os/src/server/trace_fs/main.cc @@ -658,22 +658,36 @@ class Trace_fs::Session_component : public Session_rpc_object switch (packet.operation()) { case Packet_descriptor::READ: - if (content && (packet.length() <= packet.size())) - res_length = open_node.node().read((char *)content, length, packet.position()); + if (content && (packet.length() <= packet.size())) { + Locked_ptr node { open_node.node() }; + if (!node.valid()) + break; + res_length = node->read((char *)content, length, packet.position()); + } break; case Packet_descriptor::WRITE: - if (content && (packet.length() <= packet.size())) - res_length = open_node.node().write((char const *)content, length, packet.position()); + if (content && (packet.length() <= packet.size())) { + Locked_ptr node { open_node.node() }; + if (!node.valid()) + break; + res_length = node->write((char const *)content, length, packet.position()); + } break; - case Packet_descriptor::CONTENT_CHANGED: + case Packet_descriptor::CONTENT_CHANGED: { open_node.register_notify(*tx_sink()); + /* notify_listeners may bounce the packet back*/ - open_node.node().notify_listeners(); + Locked_ptr node { open_node.node() }; + if (!node.valid()) + return; + node->notify_listeners(); + /* otherwise defer acknowledgement of this packet */ return; - + } + case Packet_descriptor::READ_READY: /* not supported */ break; @@ -824,17 +838,20 @@ class Trace_fs::Session_component : public Session_rpc_object auto file_fn = [&] (Open_node &open_node) { - Node &dir = open_node.node(); + Locked_ptr dir { open_node.node() }; + + if (!dir.valid()) + throw Invalid_handle(); if (create) throw Permission_denied(); - File *file = dynamic_cast(dir.lookup(name.string())); + File *file = dynamic_cast(dir->lookup(name.string())); if (!file) throw Invalid_name(); Open_node *open_file = - new (_md_alloc) Open_node(*file, _open_node_registry); + new (_md_alloc) Open_node(file->weak_ptr(), _open_node_registry); return open_file->id(); }; @@ -871,7 +888,7 @@ class Trace_fs::Session_component : public Session_rpc_object throw Invalid_name(); Open_node *open_dir = - new (_md_alloc) Open_node(*dir, _open_node_registry); + new (_md_alloc) Open_node(dir->weak_ptr(), _open_node_registry); return Dir_handle { open_dir->id().value }; } @@ -885,7 +902,7 @@ class Trace_fs::Session_component : public Session_rpc_object Node *node = _root_dir.lookup(path_str + 1); Open_node *open_node = - new (_md_alloc) Open_node(*node, _open_node_registry); + new (_md_alloc) Open_node(node->weak_ptr(), _open_node_registry); return open_node->id(); } @@ -894,26 +911,29 @@ class Trace_fs::Session_component : public Session_rpc_object { auto close_fn = [&] (Open_node &open_node) { - Node &node = open_node.node(); + Locked_ptr node { open_node.node() }; + + if (!node.valid()) + throw Invalid_handle(); /** * Acknowledge the change of the content of files which may be * modified by the user of the file system. */ - Changeable_content *changeable = dynamic_cast(&node); + Changeable_content *changeable = dynamic_cast(&*node); if (changeable) { if (changeable->changed()) { changeable->acknowledge_change(); /* let the trace fs perform the provoked actions */ - _trace_fs->handle_changed_node(&node); + _trace_fs->handle_changed_node(&*node); } } /* * Notify listeners about the changed file. */ - node.notify_listeners(); + node->notify_listeners(); /* * De-allocate handle @@ -931,7 +951,10 @@ class Trace_fs::Session_component : public Session_rpc_object Status status(Node_handle node_handle) { auto status_fn = [&] (Open_node &open_node) { - return open_node.node().status(); + Locked_ptr node { open_node.node() }; + if (!node.valid()) + throw Invalid_handle(); + return node->status(); }; try { @@ -947,7 +970,10 @@ class Trace_fs::Session_component : public Session_rpc_object void truncate(File_handle handle, file_size_t size) { auto truncate_fn = [&] (Open_node &open_node) { - open_node.node().truncate(size); + Locked_ptr node { open_node.node() }; + if (!node.valid()) + throw Invalid_handle(); + node->truncate(size); }; try { diff --git a/repos/os/src/server/trace_fs/node.h b/repos/os/src/server/trace_fs/node.h index ed7d263800..17785d99d6 100644 --- a/repos/os/src/server/trace_fs/node.h +++ b/repos/os/src/server/trace_fs/node.h @@ -26,7 +26,8 @@ namespace Trace_fs { class Node; } -class Trace_fs::Node : public Node_base, public List::Element +class Trace_fs::Node : public Node_base, public Weak_object, + public List::Element { public: @@ -52,6 +53,8 @@ class Trace_fs::Node : public Node_base, public List::Element : _inode(_unique_inode()) { _name[0] = 0; } + virtual ~Node() { lock_for_destruction(); } + unsigned long inode() const { return _inode; } char const *name() const { return _name; }