diff --git a/repos/os/include/file_system_session/connection.h b/repos/os/include/file_system_session/connection.h index 691e350658..cea72bd4f0 100644 --- a/repos/os/include/file_system_session/connection.h +++ b/repos/os/include/file_system_session/connection.h @@ -57,7 +57,7 @@ struct File_system::Connection_base : Genode::Connection, Session_clien "label=\"%s\", " "root=\"%s\", " "writeable=%d", - 4*1024*sizeof(long) + tx_buf_size, + 8*1024*sizeof(long) + tx_buf_size, tx_buf_size, label, root, writeable)), Session_client(cap(), tx_block_alloc) 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 de537a136b..88bf143269 100644 --- a/repos/os/include/file_system_session/file_system_session.h +++ b/repos/os/include/file_system_session/file_system_session.h @@ -185,8 +185,9 @@ struct File_system::Control { /* to manipulate the executable bit */ }; struct File_system::Directory_entry { enum Type { TYPE_FILE, TYPE_DIRECTORY, TYPE_SYMLINK }; - Type type; - char name[MAX_NAME_LEN]; + unsigned inode; + Type type; + char name[MAX_NAME_LEN]; }; diff --git a/repos/os/run/vfs_stress_fs.run b/repos/os/run/vfs_stress_fs.run index c412170057..7a7ea26136 100644 --- a/repos/os/run/vfs_stress_fs.run +++ b/repos/os/run/vfs_stress_fs.run @@ -12,13 +12,15 @@ install_config { - - - - + + + + + + diff --git a/repos/os/run/vfs_stress_ram.run b/repos/os/run/vfs_stress_ram.run index 62d2fefb56..7b98523158 100644 --- a/repos/os/run/vfs_stress_ram.run +++ b/repos/os/run/vfs_stress_ram.run @@ -12,13 +12,15 @@ install_config { - - - - + + + + + + diff --git a/repos/os/src/server/vfs/assert.h b/repos/os/src/server/vfs/assert.h index 9ed248f1e3..bbf33574b1 100644 --- a/repos/os/src/server/vfs/assert.h +++ b/repos/os/src/server/vfs/assert.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2015 Genode Labs GmbH + * Copyright (C) 2015-2016 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. @@ -18,6 +18,12 @@ #include #include +static bool const verbose = false; +#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__) +#define PERRV(...) if (verbose) PERR(__VA_ARGS__) + +#define PERR_THROW(e) PERRV("%s - %s", __func__, #e); throw e(); + namespace File_system { using namespace Vfs; @@ -27,11 +33,11 @@ namespace File_system { typedef Directory_service::Mkdir_result Result; switch (r) { - case Result::MKDIR_ERR_NAME_TOO_LONG: throw Name_too_long(); - case Result::MKDIR_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::MKDIR_ERR_NO_SPACE: throw No_space(); - case Result::MKDIR_ERR_NO_PERM: throw Permission_denied(); - case Result::MKDIR_ERR_EXISTS: throw Node_already_exists(); + case Result::MKDIR_ERR_NAME_TOO_LONG: PERR_THROW(Name_too_long); + case Result::MKDIR_ERR_NO_ENTRY: PERR_THROW(Lookup_failed); + case Result::MKDIR_ERR_NO_SPACE: PERR_THROW(No_space); + case Result::MKDIR_ERR_NO_PERM: PERR_THROW(Permission_denied); + case Result::MKDIR_ERR_EXISTS: PERR_THROW(Node_already_exists); case Result::MKDIR_OK: break; } } @@ -41,11 +47,11 @@ namespace File_system { typedef Directory_service::Open_result Result; switch (r) { - case Result::OPEN_ERR_NAME_TOO_LONG: throw Invalid_name(); - case Result::OPEN_ERR_UNACCESSIBLE: throw Lookup_failed(); - case Result::OPEN_ERR_NO_SPACE: throw No_space(); - case Result::OPEN_ERR_NO_PERM: throw Permission_denied(); - case Result::OPEN_ERR_EXISTS: throw Node_already_exists(); + case Result::OPEN_ERR_NAME_TOO_LONG: PERR_THROW(Invalid_name); + case Result::OPEN_ERR_UNACCESSIBLE: PERR_THROW(Lookup_failed); + case Result::OPEN_ERR_NO_SPACE: PERR_THROW(No_space); + case Result::OPEN_ERR_NO_PERM: PERR_THROW(Permission_denied); + case Result::OPEN_ERR_EXISTS: PERR_THROW(Node_already_exists); case Result::OPEN_OK: break; } } @@ -55,23 +61,34 @@ namespace File_system { typedef Directory_service::Symlink_result Result; switch (r) { - case Result::SYMLINK_ERR_NAME_TOO_LONG: throw Invalid_name(); - case Result::SYMLINK_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::SYMLINK_ERR_NO_SPACE: throw No_space(); - case Result::SYMLINK_ERR_NO_PERM: throw Permission_denied(); - case Result::SYMLINK_ERR_EXISTS: throw Node_already_exists(); + case Result::SYMLINK_ERR_NAME_TOO_LONG: PERR_THROW(Invalid_name); + case Result::SYMLINK_ERR_NO_ENTRY: PERR_THROW(Lookup_failed); + case Result::SYMLINK_ERR_NO_SPACE: PERR_THROW(No_space); + case Result::SYMLINK_ERR_NO_PERM: PERR_THROW(Permission_denied); + case Result::SYMLINK_ERR_EXISTS: PERR_THROW(Node_already_exists); case Result::SYMLINK_OK: break; } } + static inline void assert_readlink(Directory_service::Readlink_result r) + { + typedef Directory_service::Readlink_result Result; + + switch (r) { + case Result::READLINK_ERR_NO_ENTRY: PERR_THROW(Lookup_failed); + case Result::READLINK_ERR_NO_PERM: PERR_THROW(Permission_denied); + case Result::READLINK_OK: break; + } + } + static inline void assert_truncate(File_io_service::Ftruncate_result r) { typedef File_io_service::Ftruncate_result Result; switch (r) { - case Result::FTRUNCATE_ERR_INTERRUPT: throw Invalid_handle(); - case Result::FTRUNCATE_ERR_NO_SPACE: throw No_space(); - case Result::FTRUNCATE_ERR_NO_PERM: throw Permission_denied(); + case Result::FTRUNCATE_ERR_INTERRUPT: PERR_THROW(Invalid_handle); + case Result::FTRUNCATE_ERR_NO_SPACE: PERR_THROW(No_space); + case Result::FTRUNCATE_ERR_NO_PERM: PERR_THROW(Permission_denied); case Result::FTRUNCATE_OK: break; } } @@ -80,9 +97,9 @@ namespace File_system { { typedef Directory_service::Unlink_result Result; switch (r) { - case Result::UNLINK_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::UNLINK_ERR_NO_PERM: throw Permission_denied(); - case Result::UNLINK_ERR_NOT_EMPTY: throw Not_empty(); + case Result::UNLINK_ERR_NO_ENTRY: PERR_THROW(Lookup_failed); + case Result::UNLINK_ERR_NO_PERM: PERR_THROW(Permission_denied); + case Result::UNLINK_ERR_NOT_EMPTY: PERR_THROW(Not_empty); case Result::UNLINK_OK: break; } } @@ -91,7 +108,8 @@ namespace File_system { { typedef Directory_service::Stat_result Result; switch (r) { - case Result::STAT_ERR_NO_ENTRY: throw Lookup_failed(); + case Result::STAT_ERR_NO_ENTRY: PERR_THROW(Lookup_failed); + case Result::STAT_ERR_NO_PERM: PERR_THROW(Permission_denied); case Result::STAT_OK: break; } } @@ -100,9 +118,9 @@ namespace File_system { { typedef Directory_service::Rename_result Result; switch (r) { - case Result::RENAME_ERR_NO_ENTRY: throw Lookup_failed(); - case Result::RENAME_ERR_CROSS_FS: throw Permission_denied(); - case Result::RENAME_ERR_NO_PERM: throw Permission_denied(); + case Result::RENAME_ERR_NO_ENTRY: PERR_THROW(Lookup_failed); + case Result::RENAME_ERR_CROSS_FS: PERR_THROW(Permission_denied); + case Result::RENAME_ERR_NO_PERM: PERR_THROW(Permission_denied); case Result::RENAME_OK: break; } } diff --git a/repos/os/src/server/vfs/main.cc b/repos/os/src/server/vfs/main.cc index c8d0ae9053..43edef9e60 100644 --- a/repos/os/src/server/vfs/main.cc +++ b/repos/os/src/server/vfs/main.cc @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2015 Genode Labs GmbH + * Copyright (C) 2015-2016 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. @@ -13,48 +13,118 @@ /* Genode includes */ #include +#include #include -#include #include #include #include #include #include +#include /* Local includes */ #include "assert.h" -#include "node_cache.h" -#include "node_handle_registry.h" +#include "node.h" -namespace File_system { +namespace Vfs_server { - using namespace Genode; + using namespace File_system; using namespace Vfs; class Session_component; class Root; struct Main; - typedef Genode::Path Subpath; - - Vfs::File_system *root() + static Genode::Xml_node vfs_config() { - static Vfs::Dir_file_system _root(config()->xml_node().sub_node("vfs"), - Vfs::global_file_system_factory()); - - return &_root; + try { return Genode::config()->xml_node().sub_node("vfs"); } + catch (...) { + PERR("vfs not configured"); + Genode::env()->parent()->exit(~0); + Genode::sleep_forever(); + } } }; -class File_system::Session_component : public Session_rpc_object +class Vfs_server::Session_component : + public File_system::Session_rpc_object { private: - Node_handle_registry _handle_registry; - Subpath const _root_path; - Signal_rpc_member _process_packet_dispatcher; - bool _writable; + /* maximum number of open nodes per session */ + enum { MAX_NODE_HANDLES = 128U }; + + Node *_nodes[MAX_NODE_HANDLES]; + + /** + * Each open node handle can act as a listener to be informed about + * node changes. + */ + Listener _listeners[MAX_NODE_HANDLES]; + + Genode::String<160> _label; + + Genode::Ram_connection _ram = { _label.string() }; + Genode::Heap _alloc = + { &_ram, Genode::env()->rm_session() }; + + Genode::Signal_rpc_member + _process_packet_dispatcher; + Vfs::Dir_file_system &_vfs; + Directory _root; + bool _writable; + + + /**************************** + ** Handle to node mapping ** + ****************************/ + + bool _in_range(int handle) const { + return ((handle >= 0) && (handle < MAX_NODE_HANDLES)); + } + + int _next_slot() + { + for (int i = 1; i < MAX_NODE_HANDLES; ++i) + if (_nodes[i] == nullptr) + return i; + + throw Out_of_metadata(); + } + + /** + * Lookup node using its handle as key + */ + Node *_lookup_node(Node_handle handle) { + return _in_range(handle.value) ? _nodes[handle.value] : 0; } + + /** + * Lookup typed node using its handle as key + * + * \throw Invalid_handle + */ + template + typename Node_type::Type &_lookup(HANDLE_TYPE handle) + { + if (!_in_range(handle.value)) + throw Invalid_handle(); + + typedef typename Node_type::Type Node; + Node *node = dynamic_cast(_nodes[handle.value]); + if (!node) + throw Invalid_handle(); + + return *node; + } + + bool _refer_to_same_node(Node_handle h1, Node_handle h2) const + { + if (!(_in_range(h1.value) && _in_range(h2.value))) + throw Invalid_handle(); + + return _nodes[h1.value] == _nodes[h2.value]; + } /****************************** @@ -68,9 +138,10 @@ class File_system::Session_component : public Session_rpc_object { void * const content = tx_sink()->packet_content(packet); size_t const length = packet.length(); - seek_off_t const offset = packet.position(); + seek_off_t const seek = packet.position(); if ((!(content && length)) || (packet.length() > packet.size())) { + PDBGV("bad packet %d: %llu:%zu", packet.handle().value, packet.position(), packet.length()); packet.succeeded(false); return; } @@ -81,18 +152,20 @@ class File_system::Session_component : public Session_rpc_object switch (packet.operation()) { case Packet_descriptor::READ: { - Node *node = _handle_registry.lookup_read(packet.handle()); - if (!node) return; - Node_lock_guard guard(node); - res_length = node->read((char *)content, length, offset); + Node *node = _lookup_node(packet.handle()); + if (!(node && (node->mode&READ_ONLY))) + return; + + res_length = node->read(_vfs, (char *)content, length, seek); break; } case Packet_descriptor::WRITE: { - Node *node = _handle_registry.lookup_write(packet.handle()); - if (!node) return; - Node_lock_guard guard(node); - res_length = node->write((char const *)content, length, offset); + Node *node = _lookup_node(packet.handle()); + if (!(node && (node->mode&WRITE_ONLY))) + return; + + res_length = node->write(_vfs, (char const *)content, length, seek); break; } } @@ -150,15 +223,18 @@ class File_system::Session_component : public Session_rpc_object * 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(); } + if (path[0] != '/') throw Lookup_failed(); } /** * Check if string represents a valid name (must not contain '/') */ - static void _assert_valid_name(char const *name) { + 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(); } + throw Invalid_name(); + } public: @@ -170,16 +246,19 @@ class File_system::Session_component : public Session_rpc_object * \param root_path path root of the session * \param writable whether the session can modify files */ - Session_component(Server::Entrypoint &ep, - Node_cache &cache, - size_t tx_buf_size, - Subpath const &root_path, - bool writable) + Session_component(Server::Entrypoint &ep, + char const *label, + size_t ram_quota, + size_t tx_buf_size, + Vfs::Dir_file_system &vfs, + char const *root_path, + bool writable) : Session_rpc_object(env()->ram_session()->alloc(tx_buf_size), ep.rpc_ep()), - _handle_registry(cache), - _root_path(root_path.base()), + _label(label), _process_packet_dispatcher(ep, *this, &Session_component::_process_packets), + _vfs(vfs), + _root(vfs, root_path, false), _writable(writable) { /* @@ -188,6 +267,18 @@ class File_system::Session_component : public Session_rpc_object */ _tx.sigh_packet_avail(_process_packet_dispatcher); _tx.sigh_ready_to_ack(_process_packet_dispatcher); + + /* + * the '/' node is not dynamically allocated, so it is + * permanently bound to Dir_handle(0); + */ + _nodes[0] = &_root; + for (unsigned i = 1; i < MAX_NODE_HANDLES; ++i) + _nodes[i] = nullptr; + + _ram.ref_account(Genode::env()->ram_session_cap()); + Genode::env()->ram_session()->transfer_quota(_ram.cap(), ram_quota); + PWRN("ram quota starts at %zd for %s", _ram.quota(), _label.string()); } /** @@ -196,7 +287,15 @@ class File_system::Session_component : public Session_rpc_object ~Session_component() { Dataspace_capability ds = tx_sink()->dataspace(); - env()->ram_session()->free(static_cap_cast(ds)); + env()->ram_session()->free(static_cap_cast(ds)); + } + + void upgrade(char const *args) + { + size_t new_quota = + Genode::Arg_string::find_arg(args, "ram_quota").ulong_value(0); + Genode::env()->ram_session()->transfer_quota(_ram.cap(), new_quota); + PWRN("ram quota upgraded to %zd for %s", _ram.quota(), _label.string()); } @@ -204,91 +303,161 @@ class File_system::Session_component : public Session_rpc_object ** File_system interface ** ***************************/ - Dir_handle dir(Path const &path, bool create) override + Dir_handle dir(File_system::Path const &path, bool create) override { - if ((!_writable) && create) + PDBGV("%s create=%d", path.string(), create); + + if (create && (!_writable)) throw Permission_denied(); char const *path_str = path.string(); + /* '/' is bound to '0' */ + if (!strcmp(path_str, "/")) { + if (create) throw Node_already_exists(); + return Dir_handle(0); + } + _assert_valid_path(path_str); + Vfs_server::Path fullpath(_root.path()); + fullpath.append(path_str); + path_str = fullpath.base(); - /* re-root the path */ - Subpath dir_path(path_str+1, _root_path.base()); - path_str = dir_path.base(); + /* make sure a handle is free before allocating */ + auto slot = _next_slot(); - return _handle_registry.directory(path_str, create); + if (!create && !_vfs.is_directory(path_str)) + throw Lookup_failed(); + + Directory *dir; + try { dir = new (_alloc) Directory(_vfs, path_str, create); } + catch (Out_of_memory) { throw Out_of_metadata(); } + + _nodes[slot] = dir; + return Dir_handle(slot); } File_handle file(Dir_handle dir_handle, Name const &name, Mode fs_mode, bool create) override { - if ((!_writable) && - (create || (fs_mode != STAT_ONLY && fs_mode != READ_ONLY))) - throw Permission_denied(); + PDBGV("%d:%s create=%d", dir_handle.value, name.string(), create); + + if ((create || (fs_mode & WRITE_ONLY)) && (!_writable)) + throw Permission_denied(); + + Directory &dir = _lookup(dir_handle); char const *name_str = name.string(); _assert_valid_name(name_str); - Directory *dir = _handle_registry.lookup_and_lock(dir_handle); - Node_lock_guard dir_guard(dir); + /* make sure a handle is free before allocating */ + auto slot = _next_slot(); - /* re-root the path */ - Subpath subpath(name_str, dir->path()); - char *path_str = subpath.base(); - File_handle handle = _handle_registry.file(path_str, fs_mode, create); - if (create) - dir->mark_as_updated(); - return handle; + File *file = dir.file(_vfs, _alloc, name_str, fs_mode, create); + + _nodes[slot] = file; + return File_handle(slot); } Symlink_handle symlink(Dir_handle dir_handle, Name const &name, bool create) override { + PDBGV("%d:%s create=%d", dir_handle.value, name.string(), create); + if (create && !_writable) throw Permission_denied(); + Directory &dir = _lookup(dir_handle); + char const *name_str = name.string(); _assert_valid_name(name_str); - Directory *dir = _handle_registry.lookup_and_lock(dir_handle); - Node_lock_guard dir_guard(dir); + /* make sure a handle is free before allocating */ + auto slot = _next_slot(); - /* re-root the path */ - Subpath subpath(name_str, dir->path()); - char *path_str = subpath.base(); + Symlink *link = dir.symlink(_vfs, _alloc, name_str, + _writable ? READ_WRITE : READ_ONLY, create); - Symlink_handle handle = _handle_registry.symlink( - path_str, (_writable ? READ_WRITE : READ_ONLY), create); - if (create) - dir->mark_as_updated(); - return handle; + _nodes[slot] = link; + return Symlink_handle(slot); } - Node_handle node(Path const &path) override + Node_handle node(File_system::Path const &path) override { + PDBGV("%s", path.string()); + char const *path_str = path.string(); + /* '/' is bound to '0' */ + if (!strcmp(path_str, "/")) + return Node_handle(0); + _assert_valid_path(path_str); /* re-root the path */ - Subpath sub_path(path_str+1, _root_path.base()); + Path sub_path(path_str+1, _root.path()); path_str = sub_path.base(); + if (!_vfs.leaf_path(path_str)) + throw Lookup_failed(); - return _handle_registry.node(path_str); + auto slot = _next_slot(); + Node *node; + + try { node = new (_alloc) Node(path_str, STAT_ONLY); } + catch (Out_of_memory) { throw Out_of_metadata(); } + + _nodes[slot] = node; + return Node_handle(slot); } - void close(Node_handle handle) { _handle_registry.free(handle); } + void close(Node_handle handle) override + { + PDBGV("%d", handle.value); + + /* handle '0' cannot be freed */ + if (!handle.value) { + _root.notify_listeners(); + return; + } + + if (!_in_range(handle.value)) + return; + + Node *node = _nodes[handle.value]; + if (!node) { return; } + + node->notify_listeners(); + + /* + * De-allocate handle + */ + Listener &listener = _listeners[handle.value]; + + if (listener.valid()) + node->remove_listener(&listener); + + if (File *file = dynamic_cast(node)) + destroy(_alloc, file); + else if (Directory *dir = dynamic_cast(node)) + destroy(_alloc, dir); + else if (Symlink *link = dynamic_cast(node)) + destroy(_alloc, link); + else + destroy(_alloc, node); + + _nodes[handle.value] = 0; + listener = Listener(); + } Status status(Node_handle node_handle) { + PDBGV("%d", node_handle.value); + Directory_service::Stat vfs_stat; File_system::Status fs_stat; - Node *node; - try { node = _handle_registry.lookup_and_lock(node_handle); } - catch (Invalid_handle) { return fs_stat; } - Node_lock_guard guard(node); - char const *path_str = node->path(); + Node &node = _lookup(node_handle); - if (root()->stat(path_str, vfs_stat) != Directory_service::STAT_OK) + if (_vfs.stat(node.path(), vfs_stat) != Directory_service::STAT_OK) { + memset(&fs_stat, 0x00, sizeof(fs_stat)); return fs_stat; + } fs_stat.inode = vfs_stat.inode; @@ -299,7 +468,7 @@ class File_system::Session_component : public Session_rpc_object case Directory_service::STAT_MODE_DIRECTORY: fs_stat.mode = File_system::Status::MODE_DIRECTORY; - fs_stat.size = root()->num_dirent(path_str) * sizeof(Directory_entry); + fs_stat.size = _vfs.num_dirent(node.path()) * sizeof(Directory_entry); return fs_stat; case Directory_service::STAT_MODE_SYMLINK: @@ -315,56 +484,36 @@ class File_system::Session_component : public Session_rpc_object return fs_stat; } - /** - * Set information about an open file or directory - */ - void control(Node_handle, Control) { } - - /** - * Delete file or directory - */ void unlink(Dir_handle dir_handle, Name const &name) { + PDBGV("%d:%s", dir_handle.value, name.string()); + if (!_writable) throw Permission_denied(); - char const *str = name.string(); - _assert_valid_name(str); + Directory &dir = _lookup(dir_handle); - Directory *dir = _handle_registry.lookup_and_lock(dir_handle); - Node_lock_guard guard(dir); + char const *name_str = name.string(); + _assert_valid_name(name_str); - Subpath path(str, dir->path()); - str = path.base(); + Path path(name_str, dir.path()); - /* - * If the file is open in any other session, - * unlinking is not allowed. This is to prevent - * dangling pointers in handle registries. - */ - if (_handle_registry.is_open(str)) - throw Permission_denied(); - - assert_unlink(root()->unlink(str)); - dir->mark_as_updated(); + assert_unlink(_vfs.unlink(path.base())); + dir.mark_as_updated(); } - /** - * Truncate or grow file to specified size - */ void truncate(File_handle file_handle, file_size_t size) { - File *file = _handle_registry.lookup_and_lock(file_handle); - Node_lock_guard guard(file); - file->truncate(size); - file->mark_as_updated(); + PDBGV("%d", file_handle.value); + _lookup(file_handle).truncate(size); } - /** - * Move and rename directory entry - */ void move(Dir_handle from_dir_handle, Name const &from_name, Dir_handle to_dir_handle, Name const &to_name) { + PDBGV("%d:%s - %d:%s", + from_dir_handle.value, from_name.string(), + to_dir_handle.value, to_name.string()); + if (!_writable) throw Permission_denied(); @@ -374,108 +523,118 @@ class File_system::Session_component : public Session_rpc_object _assert_valid_name(from_str); _assert_valid_name( to_str); - if (_handle_registry.refer_to_same_node(from_dir_handle, to_dir_handle)) { - Directory *dir = _handle_registry.lookup_and_lock(from_dir_handle); - Node_lock_guard from_guard(dir); + Directory &from_dir = _lookup(from_dir_handle); + Directory &to_dir = _lookup( to_dir_handle); - from_str = Subpath(from_str, dir->path()).base(); - to_str = Subpath( to_str, dir->path()).base(); + Path from_path(from_str, from_dir.path()); + Path to_path( to_str, to_dir.path()); - if (_handle_registry.is_open(to_str)) - throw Permission_denied(); + assert_rename(_vfs.rename(from_path.base(), to_path.base())); - assert_rename(root()->rename(from_str, to_str)); - _handle_registry.rename(from_str, to_str); - - dir->mark_as_updated(); - return; - } - - Directory *from_dir = _handle_registry.lookup_and_lock(from_dir_handle); - Node_lock_guard from_guard(from_dir); - - Directory *to_dir = _handle_registry.lookup_and_lock( to_dir_handle); - Node_lock_guard to_guard(to_dir); - - from_str = Subpath(from_str, from_dir->path()).base(); - to_str = Subpath( to_str, to_dir->path()).base(); - if (_handle_registry.is_open(to_str)) - throw Permission_denied(); - - assert_rename(root()->rename(from_str, to_str)); - _handle_registry.rename(from_str, to_str); - - from_dir->mark_as_updated(); - to_dir->mark_as_updated(); + from_dir.mark_as_updated(); + to_dir.mark_as_updated(); } - void sigh(Node_handle node_handle, Signal_context_capability sigh) { - _handle_registry.sigh(node_handle, sigh); } + void sigh(Node_handle handle, Signal_context_capability sigh) override + { + PDBGV("%d", handle.value); + if (!_in_range(handle.value)) + throw Invalid_handle(); + + Node *node = dynamic_cast(_nodes[handle.value]); + if (!node) + throw Invalid_handle(); + + Listener &listener = _listeners[handle.value]; + + /* + * If there was already a handler registered for the node, + * remove the old handler. + */ + if (listener.valid()) + node->remove_listener(&listener); + + /* + * Register new handler + */ + listener = Listener(sigh); + node->add_listener(&listener); + } /** - * Sync the VFS and send any pending signal on the node. + * Sync the VFS and send any pending signals on the node. */ void sync(Node_handle handle) { - Node *node; + PDBGV("%d", handle.value); try { - node = _handle_registry.lookup_and_lock(handle); - } catch (Invalid_handle) { - return; - } - Node_lock_guard guard(node); - - root()->sync(node->path()); - node->notify_listeners(); + Node &node = _lookup(handle); + _vfs.sync(node.path()); + node.notify_listeners(); + } catch (Invalid_handle) { } } + + void control(Node_handle, Control) { } }; -class File_system::Root : public Root_component +class Vfs_server::Root : + public Genode::Root_component { private: - Node_cache _cache; + Vfs::Dir_file_system _vfs = + { vfs_config(), Vfs::global_file_system_factory() }; + Server::Entrypoint &_ep; protected: Session_component *_create_session(const char *args) override { - Subpath session_root; + using namespace Genode; + + Path session_root; bool writeable = false; Session_label const label(args); + char tmp[MAX_PATH_LEN]; try { Session_policy policy(label); - char buf[MAX_PATH_LEN]; /* Determine the session root directory. * Defaults to '/' if not specified by session * policy or session arguments. */ try { - policy.attribute("root").value(buf, MAX_PATH_LEN); - session_root.append(buf); + policy.attribute("root").value(tmp, sizeof(tmp)); + session_root.import(tmp, "/"); } catch (Xml_node::Nonexistent_attribute) { } - Arg_string::find_arg(args, "root").string(buf, MAX_PATH_LEN, "/"); - session_root.append(buf); - /* Determine if the session is writeable. * Policy overrides arguments, 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) { - PERR("rejecting session request, no matching policy for %s", label.string()); - throw Root::Unavailable(); + } catch (Session_policy::No_policy_defined) { } + + Arg_string::find_arg(args, "root").string(tmp, sizeof(tmp), "/"); + if (Genode::strcmp("/", tmp, sizeof(tmp))) { + session_root.append("/"); + session_root.append(tmp); } - size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); - size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0); + /* + * If no policy matches the client gets + * read-only access to the root. + */ + + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota").aligned_size(); + size_t tx_buf_size = + Arg_string::find_arg(args, "tx_buf_size").aligned_size(); if (!tx_buf_size) throw Root::Invalid_args(); @@ -484,25 +643,42 @@ class File_system::Root : public Root_component * Check if donated ram quota suffices for session data, * and communication buffer. */ - size_t session_size = sizeof(Session_component) + tx_buf_size; - if (max((size_t)4096, session_size) > ram_quota) { + size_t session_size = + max((size_t)4096, sizeof(Session_component)) + + tx_buf_size; + + if (session_size > ram_quota) { PERR("insufficient 'ram_quota' from %s, got %zd, need %zd", label.string(), ram_quota, session_size); throw Root::Quota_exceeded(); } + ram_quota -= session_size; /* check if the session root exists */ - if (!root()->is_directory(session_root.base())) { + if (!((session_root == "/") || _vfs.is_directory(session_root.base()))) { PERR("session root '%s' not found for '%s'", session_root.base(), label.string()); throw Root::Unavailable(); } Session_component *session = new(md_alloc()) - Session_component(_ep, _cache, tx_buf_size, session_root, writeable); + Session_component(_ep, + label.string(), + ram_quota, + tx_buf_size, + _vfs, + session_root.base(), + writeable); + PLOG("session opened for '%s' at '%s'", label.string(), session_root.base()); return session; } + void _upgrade_session(Session_component *session, + char const *args) override + { + session->upgrade(args); + } + public: /** @@ -511,7 +687,7 @@ class File_system::Root : public Root_component * \param ep entrypoint * \param md_alloc meta-data allocator */ - Root(Server::Entrypoint &ep, Allocator &md_alloc) + Root(Server::Entrypoint &ep, Genode::Allocator &md_alloc) : Root_component(&ep.rpc_ep(), &md_alloc), _ep(ep) @@ -519,16 +695,17 @@ class File_system::Root : public Root_component }; -struct File_system::Main +struct Vfs_server::Main { Server::Entrypoint &ep; /* * Initialize root interface */ - Sliced_heap sliced_heap = { env()->ram_session(), env()->rm_session() }; + Genode::Sliced_heap sliced_heap = + { Genode::env()->ram_session(), Genode::env()->rm_session() }; - Root fs_root = { ep, sliced_heap }; + Vfs_server::Root fs_root = { ep, sliced_heap }; Main(Server::Entrypoint &ep) : ep(ep) { @@ -546,5 +723,7 @@ char const * Server::name() { return "vfs_ep"; } Genode::size_t Server::stack_size() { return 2*1024*sizeof(long); } -void Server::construct(Server::Entrypoint &ep) { - static File_system::Main inst(ep); } +void Server::construct(Server::Entrypoint &ep) +{ + static Vfs_server::Main inst(ep); +} diff --git a/repos/os/src/server/vfs/node.h b/repos/os/src/server/vfs/node.h new file mode 100644 index 0000000000..ba3b08cc83 --- /dev/null +++ b/repos/os/src/server/vfs/node.h @@ -0,0 +1,273 @@ +/* + * \brief Internal nodes of VFS server + * \author Emery Hemingway + * \date 2016-03-29 + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _VFS__NODE_H_ +#define _VFS__NODE_H_ + +/* Genode includes */ +#include +#include +#include + +/* Local includes */ +#include "assert.h" + +namespace Vfs_server { + + using namespace File_system; + using namespace Vfs; + + struct Node; + struct Directory; + struct File; + struct Symlink; + + /* Vfs::MAX_PATH is shorter than File_system::MAX_PATH */ + enum { MAX_PATH_LEN = Vfs::MAX_PATH_LEN }; + + typedef Genode::Path Path; + + typedef Genode::Allocator::Out_of_memory Out_of_memory; + + /** + * Type trait for determining the node type for a given handle type + */ + template struct Node_type; + template<> struct Node_type { typedef Node Type; }; + template<> struct Node_type { typedef Directory Type; }; + template<> struct Node_type { typedef File Type; }; + template<> struct Node_type { typedef Symlink Type; }; + + + /** + * Type trait for determining the handle type for a given node type + */ + template struct Handle_type; + template<> struct Handle_type { typedef Node_handle Type; }; + template<> struct Handle_type { typedef Dir_handle Type; }; + template<> struct Handle_type { typedef File_handle Type; }; + template<> struct Handle_type { typedef Symlink_handle Type; }; + + /* + * Note that the file objects are created at the + * VFS in the local node constructors, this is to + * ensure that Out_of_metadata is thrown before + * the VFS is modified. + */ +} + + +struct Vfs_server::Node : File_system::Node_base +{ + Path const _path; + Mode const mode; + + Node(char const *node_path, Mode node_mode) + : _path(node_path), mode(node_mode) { } + + virtual ~Node() { } + + char const *path() { return _path.base(); } + + virtual size_t read(Vfs::File_system&, char*, size_t, seek_off_t) { return 0; } + virtual size_t write(Vfs::File_system&, char const*, size_t, seek_off_t) { return 0; } + +}; + +struct Vfs_server::Symlink : Node +{ + Symlink(Vfs::File_system &vfs, + char const *link_path, + Mode mode, + bool create) + : Node(link_path, mode) + { + if (create) + assert_symlink(vfs.symlink("", link_path)); + } + + + /******************** + ** Node interface ** + ********************/ + + size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset) + { + Vfs::file_size res = 0; + vfs.readlink(path(), dst, len, res); + return res; + } + + size_t write(Vfs::File_system &vfs, char const *src, size_t len, seek_off_t seek_offset) + { + /* ensure symlink gets something null-terminated */ + Genode::String target(src, len); + + if (vfs.symlink(target.string(), path()) == Directory_service::SYMLINK_OK) + return 0; + + mark_as_updated(); + notify_listeners(); + return target.length(); + } +}; + + +class Vfs_server::File : public Node +{ + private: + + Vfs::Vfs_handle *_handle; + + public: + File(Vfs::File_system &vfs, + Genode::Allocator &alloc, + char const *file_path, + Mode fs_mode, + bool create) + : Node(file_path, fs_mode) + { + unsigned vfs_mode = + (fs_mode-1) | (create ? Vfs::Directory_service::OPEN_MODE_CREATE : 0); + + assert_open(vfs.open(path(), vfs_mode, &_handle, alloc)); + } + + ~File() { _handle->ds().close(_handle); } + + void truncate(file_size_t size) + { + assert_truncate(_handle->fs().ftruncate(_handle, size)); + mark_as_updated(); + } + + + /******************** + ** Node interface ** + ********************/ + + size_t read(Vfs::File_system&, char *dst, size_t len, seek_off_t seek_offset) + { + Vfs::file_size res = 0; + + _handle->seek(seek_offset); + _handle->fs().read(_handle, dst, len, res); + return res; + } + + size_t write(Vfs::File_system&, char const *src, size_t len, seek_off_t seek_offset) + { + Vfs::file_size res = 0; + + _handle->seek(seek_offset); + _handle->fs().write(_handle, src, len, res); + if (res) + mark_as_updated(); + return res; + } +}; + + +struct Vfs_server::Directory : Node +{ + Directory(Vfs::File_system &vfs, char const *dir_path, bool create) + : Node(dir_path, READ_ONLY) + { + if (create) + assert_mkdir(vfs.mkdir(dir_path, 0)); + } + + File *file(Vfs::File_system &vfs, + Genode::Allocator &alloc, + char const *file_path, + Mode mode, + bool create) + { + Path subpath(file_path, path()); + char const *path_str = subpath.base(); + + File *file; + try { file = new (alloc) File(vfs, alloc, path_str, mode, create); } + catch (Out_of_memory) { throw Out_of_metadata(); } + if (create) + mark_as_updated(); + return file; + } + + Symlink *symlink(Vfs::File_system &vfs, + Genode::Allocator &alloc, + char const *link_path, + Mode mode, + bool create) + { + Path subpath(link_path, path()); + char const *path_str = subpath.base(); + + if (!create) { + Vfs::file_size out; + assert_readlink(vfs.readlink(path_str, nullptr, 0, out)); + } + + Symlink *link; + try { link = new (alloc) Symlink(vfs, path_str, mode, create); } + catch (Out_of_memory) { throw Out_of_metadata(); } + if (create) + mark_as_updated(); + return link; + } + + + /******************** + ** Node interface ** + ********************/ + + size_t read(Vfs::File_system &vfs, char *dst, size_t len, seek_off_t seek_offset) + { + Directory_service::Dirent vfs_dirent; + size_t blocksize = sizeof(File_system::Directory_entry); + + unsigned index = (seek_offset / blocksize); + + size_t remains = len; + + while (remains >= blocksize) { + memset(&vfs_dirent, 0x00, sizeof(vfs_dirent)); + if (vfs.dirent(path(), index++, vfs_dirent) + != Vfs::Directory_service::DIRENT_OK) + return len - remains; + + File_system::Directory_entry *fs_dirent = (Directory_entry *)dst; + fs_dirent->inode = vfs_dirent.fileno; + switch (vfs_dirent.type) { + case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY: + fs_dirent->type = File_system::Directory_entry::TYPE_DIRECTORY; + break; + case Vfs::Directory_service::DIRENT_TYPE_SYMLINK: + fs_dirent->type = File_system::Directory_entry::TYPE_SYMLINK; + break; + case Vfs::Directory_service::DIRENT_TYPE_FILE: + default: + fs_dirent->type = File_system::Directory_entry::TYPE_FILE; + break; + } + strncpy(fs_dirent->name, vfs_dirent.name, MAX_NAME_LEN); + + remains -= blocksize; + dst += blocksize; + } + return len - remains; + } +}; + + +#endif \ No newline at end of file diff --git a/repos/os/src/server/vfs/node_cache.h b/repos/os/src/server/vfs/node_cache.h deleted file mode 100644 index 8b9b9ba9b8..0000000000 --- a/repos/os/src/server/vfs/node_cache.h +++ /dev/null @@ -1,386 +0,0 @@ -/* - * \brief VFS server node cache - * \author Emery Hemingway - * \date 2015-09-02 - */ - -/* - * Copyright (C) 2015 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _VFS__NODE_CACHE_H_ -#define _VFS__NODE_CACHE_H_ - -/* Genode includes */ -#include -#include -#include -#include - -namespace File_system { - - struct Node; - struct Directory; - class File; - struct Symlink; - class Node_cache; - - typedef Genode::Avl_string Avl_path_string; - - Vfs::File_system *root(); -} - - -/** - * Reference counted node object that can be inserted - * into an AVL tree. - */ -class File_system::Node : public Node_base, public Genode::Avl_node, private Genode::Noncopyable -{ - friend class Node_cache; - - private: - - unsigned _ref_count; - char _path[Vfs::MAX_PATH_LEN]; - - protected: - - void incr() { ++_ref_count; } - void decr() { --_ref_count; } - - bool in_use() const { return _ref_count; } - - public: - - Node(char const *path) - : _ref_count(0) { - strncpy(_path, path, sizeof(_path)); } - - char const *path() const { return _path; } - void path(char const *new_path) { - strncpy(_path, new_path, sizeof(_path)); } - - virtual size_t read(char *dst, size_t len, seek_off_t) { return 0; } - virtual size_t write(char const *src, size_t len, seek_off_t) { return 0; } - - /************************ - ** Avl node interface ** - ************************/ - - bool higher(Node *n) { return (strcmp(n->_path, _path) > 0); } - - /** - * Find by path - */ - Node *find_by_path(const char *path) - { - if (strcmp(path, _path) == 0) return this; - - Node *n = Avl_node::child(strcmp(path, _path) > 0); - return n ? n->find_by_path(path) : nullptr; - } -}; - - -struct File_system::Directory : Node -{ - Directory(char const *path, bool create) - : Node(path) - { - if (create) - assert_mkdir(root()->mkdir(path, 0777)); - else if (strcmp("/", path, 2) == 0) - return; - else if (!root()->leaf_path(path)) - throw Lookup_failed(); - else if (!root()->is_directory(path)) - throw Node_already_exists(); - } - - size_t read(char *dst, size_t len, seek_off_t seek_offset) - { - Directory_service::Dirent vfs_dirent; - size_t blocksize = sizeof(File_system::Directory_entry); - - int index = (seek_offset / blocksize); - - size_t remains = len; - - while (remains >= blocksize) { - memset(&vfs_dirent, 0x00, sizeof(vfs_dirent)); - if (root()->dirent(path(), index++, vfs_dirent) - != Vfs::Directory_service::DIRENT_OK) - return len - remains; - - File_system::Directory_entry *fs_dirent = (Directory_entry *)dst; - switch (vfs_dirent.type) { - case Vfs::Directory_service::DIRENT_TYPE_DIRECTORY: - fs_dirent->type = File_system::Directory_entry::TYPE_DIRECTORY; - break; - case Vfs::Directory_service::DIRENT_TYPE_SYMLINK: - fs_dirent->type = File_system::Directory_entry::TYPE_SYMLINK; - break; - case Vfs::Directory_service::DIRENT_TYPE_FILE: - default: - fs_dirent->type = File_system::Directory_entry::TYPE_FILE; - break; - } - strncpy(fs_dirent->name, vfs_dirent.name, MAX_NAME_LEN); - - remains -= blocksize; - dst += blocksize; - } - return len - remains; - } -}; - - -class File_system::File : public Node -{ - private: - - Vfs_handle *_handle; - unsigned _mode; - - public: - - File(char const *path, Mode fs_mode, bool create) - : - Node(path), - _handle(nullptr) - { - switch (fs_mode) { - case STAT_ONLY: - case READ_ONLY: - _mode = Directory_service::OPEN_MODE_RDONLY; break; - case WRITE_ONLY: - case READ_WRITE: - _mode = Directory_service::OPEN_MODE_RDWR; break; - } - - unsigned mode = create ? - _mode | Directory_service::OPEN_MODE_CREATE : _mode; - - assert_open(root()->open(path, mode, &_handle)); - } - - ~File() { destroy(env()->heap(), _handle); } - - void open(Mode fs_mode) - { - if (_mode & Directory_service::OPEN_MODE_RDWR) return; - - unsigned mode; - switch (fs_mode) { - case WRITE_ONLY: - case READ_WRITE: - mode = Directory_service::OPEN_MODE_RDWR; break; - default: - return; - } - if (mode == _mode) return; - - Vfs_handle *new_handle = nullptr; - assert_open(root()->open(path(), mode, &new_handle)); - destroy(env()->heap(), _handle); - _handle = new_handle; - _mode = mode; - } - - void truncate(file_size_t size) { - assert_truncate(_handle->fs().ftruncate(_handle, size)); } - - size_t read(char *dst, size_t len, seek_off_t seek_offset) - { - Vfs::file_size res = 0; - _handle->seek(seek_offset); - _handle->fs().read(_handle, dst, len, res); - return res; - } - - size_t write(char const *src, size_t len, seek_off_t seek_offset) - { - Vfs::file_size res = 0; - _handle->seek(seek_offset); - _handle->fs().write(_handle, src, len, res); - mark_as_updated(); - return res; - } -}; - - -struct File_system::Symlink : Node -{ - Symlink(char const *path, bool create) - : Node(path) - { - if (create) - assert_symlink(root()->symlink("", path)); - else if (!root()->leaf_path(path)) - throw Lookup_failed(); - else { - Vfs::Directory_service::Stat s; - assert_stat(root()->stat(path, s)); - if (!(s.mode & Vfs::Directory_service::STAT_MODE_SYMLINK)) - throw Node_already_exists(); - } - } - - size_t read(char *dst, size_t len, seek_off_t seek_offset) - { - Vfs::file_size res = 0; - root()->readlink(path(), dst, len, res); - return res; - } - - size_t write(char const *src, size_t len, seek_off_t seek_offset) - { - root()->unlink(path()); - size_t n = (root()->symlink(src, path()) == Directory_service::SYMLINK_OK) - ? len : 0; - - if (n) { - mark_as_updated(); - notify_listeners(); - } - return n; - } -}; - - -/** - * This structure deduplicates nodes between sessions, without it - * the signal notifications would not propagate between sessions. - */ -struct File_system::Node_cache : Genode::Avl_tree -{ - Node *find(char const *path) { - return first() ? (Node *)first()->find_by_path(path) : nullptr; } - - void free(Node *node) - { - node->decr(); - if (node->in_use()) - return; - - remove(node); - destroy(env()->heap(), node); - } - - void remove_path(char const *path) - { - Node *node = find(path); - if (!node ) return; - - remove(node); - destroy(env()->heap(), node); - } - - void rename(char const *from, char const *to) - { - Node *node = find(to); - if (node) - throw Permission_denied(); - - node = find(from); - if (!node ) return; - - remove(node); - node->path(to); - insert(node); - node->mark_as_updated(); - } - - /** - * Return an existing node or query the VFS - * and alloc a proper node. - */ - Node *node(char const *path) - { - Node *node = find(path); - if (!node) { - Directory_service::Stat stat; - assert_stat(root()->stat(path, stat)); - - switch (stat.mode & ( - Directory_service::STAT_MODE_DIRECTORY | - Directory_service::STAT_MODE_SYMLINK | - File_system::Status::MODE_FILE)) { - - case Directory_service::STAT_MODE_DIRECTORY: - node = new (env()->heap()) Directory(path, false); - break; - - case Directory_service::STAT_MODE_SYMLINK: - node = new (env()->heap()) Symlink(path, false); - break; - - default: /* Directory_service::STAT_MODE_FILE */ - node = new (env()->heap()) File(path, READ_ONLY, false); - break; - } - insert(node); - } - node->incr(); - return node; - } - - Directory *directory(char const *path, bool create) - { - Directory *dir; - Node *node = find(path); - if (node) { - dir = dynamic_cast(node); - if (!dir) - throw Node_already_exists(); - - } else { - dir = new (env()->heap()) Directory(path, create); - insert(dir); - } - dir->incr(); - return dir; - } - - File *file(char const *path, Mode mode, bool create) - { - File *file; - Node *node = find(path); - if (node) { - file = dynamic_cast(node); - if (!file) - throw Node_already_exists(); - - file->open(mode); - - } else { - file = new (env()->heap()) File(path, mode, create); - insert(file); - } - file->incr(); - return file; - } - - Symlink *symlink(char const *path, bool create) - { - Symlink *link; - Node *node = find(path); - if (node) { - link = dynamic_cast(node); - if (!link) - throw Node_already_exists(); - - } else { - link = new (env()->heap()) Symlink(path, create); - insert(link); - } - link->incr(); - return link; - } -}; - -#endif diff --git a/repos/os/src/server/vfs/node_handle_registry.h b/repos/os/src/server/vfs/node_handle_registry.h deleted file mode 100644 index 198129670f..0000000000 --- a/repos/os/src/server/vfs/node_handle_registry.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * \brief Facility for managing the session-local node-handle namespace - * \author Norman Feske - * \date 2012-04-11 - * - * This file is derived from os/include/file_system/node_handle_registry.h. - */ - -#ifndef _VFS__NODE_HANDLE_REGISTRY_H_ -#define _VFS__NODE_HANDLE_REGISTRY_H_ - -#include - -namespace File_system { - - class Node; - class Directory; - class File; - class Symlink; - - /** - * Type trait for determining the node type for a given handle type - */ - template struct Node_type; - template<> struct Node_type { typedef Node Type; }; - template<> struct Node_type { typedef Directory Type; }; - template<> struct Node_type { typedef File Type; }; - template<> struct Node_type { typedef Symlink Type; }; - - - /** - * Type trait for determining the handle type for a given node type - */ - template struct Handle_type; - template<> struct Handle_type { typedef Node_handle Type; }; - template<> struct Handle_type { typedef Dir_handle Type; }; - template<> struct Handle_type { typedef File_handle Type; }; - template<> struct Handle_type { typedef Symlink_handle Type; }; - - - class Node_handle_registry - { - private: - - /* maximum number of open nodes per session */ - enum { MAX_NODE_HANDLES = 128U }; - - Lock mutable _lock; - Node *_nodes[MAX_NODE_HANDLES]; - - /** - * Each open node handle can act as a listener to be informed about - * node changes. - */ - Listener _listeners[MAX_NODE_HANDLES]; - - /** - * Mode information is stored here for each open node. - */ - enum Mode _modes[MAX_NODE_HANDLES]; - - /** - * A cache of open nodes shared between sessions. - */ - Node_cache &_cache; - - /** - * Allocate node handle - * - * \throw Out_of_metadata - */ - int _alloc(Node *node, Mode mode) - { - Lock::Guard guard(_lock); - - for (unsigned i = 0; i < MAX_NODE_HANDLES; i++) - if (!_nodes[i]) { - _nodes[i] = node; - _modes[i] = mode; - return i; - } - - throw Out_of_metadata(); - } - - bool _in_range(int handle) const - { - return ((handle >= 0) && (handle < MAX_NODE_HANDLES)); - } - - public: - - Node_handle_registry(Node_cache &cache) - : _cache(cache) - { - for (unsigned i = 0; i < MAX_NODE_HANDLES; i++) { - _nodes[i] = 0; - _modes[i] = STAT_ONLY; - } - } - - template - typename Handle_type::Type alloc(NODE_TYPE *node, Mode mode) - { - typedef typename Handle_type::Type Handle; - return Handle(_alloc(node, mode)); - } - - /** - * Request a file from the cache and allocate a handle. - */ - Dir_handle directory(char const *path, bool create) - { - Directory *dir = _cache.directory(path, create); - return alloc(dir, READ_ONLY); - } - - /** - * Request a directory from the cache and allocate a handle. - */ - File_handle file(char const *path, Mode mode, bool create) - { - File *file = _cache.file(path, mode, create); - return alloc(file, mode); - } - - /** - * Request a symlink from the cache and allocate a handle. - */ - Symlink_handle symlink(char const *path, Mode mode, bool create) - { - Symlink *link = _cache.symlink(path, create); - return alloc(link, mode); - } - - /** - * Request a node from the cache and allocate a handle. - */ - Node_handle node(char const *path) - { - Node *node = _cache.node(path); - return alloc(node, STAT_ONLY); - } - - /** - * Release node handle - */ - void free(Node_handle handle) - { - if (!_in_range(handle.value)) - return; - - Lock::Guard guard(_lock); - - Node *node = dynamic_cast(_nodes[handle.value]); - if (!node) { return; } - - node->lock(); - node->notify_listeners(); - - /* - * De-allocate handle - */ - Listener &listener = _listeners[handle.value]; - - if (listener.valid()) - node->remove_listener(&listener); - - _nodes[handle.value] = 0; - listener = Listener(); - - _modes[handle.value] = STAT_ONLY; - - node->unlock(); - _cache.free(node); - } - - /** - * Lookup node using its handle as key - * - * The node returned by this function is in a locked state. - * - * \throw Invalid_handle - */ - template - typename Node_type::Type *lookup_and_lock(HANDLE_TYPE handle) - { - if (!_in_range(handle.value)) - throw Invalid_handle(); - - Lock::Guard guard(_lock); - - typedef typename Node_type::Type Node; - Node *node = dynamic_cast(_nodes[handle.value]); - if (!node) - throw Invalid_handle(); - - node->lock(); - return node; - } - - /** - * Lookup a node for reading - * - * A node in a locked state or a null pointer is returned. - */ - Node *lookup_read(Node_handle handle) - { - if (!_in_range(handle.value)) - return nullptr; - - Lock::Guard guard(_lock); - - switch (_modes[handle.value]) { - case READ_ONLY: - case READ_WRITE: - break; - default: - return nullptr; - } - - Node *node = dynamic_cast(_nodes[handle.value]); - if (node) - node->lock(); - return node; - } - - /** - * Lookup a node for writing - * - * A node in a locked state or a null pointer is returned. - */ - Node *lookup_write(Node_handle handle) - { - if (!_in_range(handle.value)) - return nullptr; - - Lock::Guard guard(_lock); - - switch (_modes[handle.value]) { - case WRITE_ONLY: - case READ_WRITE: - break; - default: - return nullptr; - } - - Node *node = dynamic_cast(_nodes[handle.value]); - if (node) - node->lock(); - return node; - } - - bool refer_to_same_node(Node_handle h1, Node_handle h2) const - { - Lock::Guard guard(_lock); - - if (!(_in_range(h1.value) && _in_range(h2.value))) - throw Invalid_handle(); - - return _nodes[h1.value] == _nodes[h2.value]; - } - - /** - * Register signal handler to be notified of node changes - */ - void sigh(Node_handle handle, Signal_context_capability sigh) - { - if (!_in_range(handle.value)) - throw Invalid_handle(); - - Lock::Guard guard(_lock); - - Node *node = dynamic_cast(_nodes[handle.value]); - if (!node) - throw Invalid_handle(); - - node->lock(); - Node_lock_guard node_lock_guard(node); - - Listener &listener = _listeners[handle.value]; - - /* - * If there was already a handler registered for the node, - * remove the old handler. - */ - if (listener.valid()) - node->remove_listener(&listener); - - /* - * Register new handler - */ - listener = Listener(sigh); - node->add_listener(&listener); - } - - /** - * Remove a path from the cache. - */ - void remove(char const *path) { _cache.remove_path(path); } - - /** - * Rename a node in the cache. - */ - void rename(char const *from, char const *to) { - _cache.rename(from, to); } - - /** - * Is the node open (present in the cache)? - */ - bool is_open(char const *path) { - return _cache.find(path); } - }; -} - -#endif /* _VFS__NODE_HANDLE_REGISTRY_H_ */