From 07b36573ea06aebe8d559c42802f27a40b8c158a Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Tue, 7 Nov 2017 21:41:56 -0600 Subject: [PATCH] Restrict VFS Dir_filesystem allocations to 'opendir' only Allocations made at the client-side of the VFS must be contained within functions that may return errors indicating resource exhaustion. Fix #2569 --- repos/os/include/vfs/dir_file_system.h | 226 +++++++++++++------------ 1 file changed, 119 insertions(+), 107 deletions(-) diff --git a/repos/os/include/vfs/dir_file_system.h b/repos/os/include/vfs/dir_file_system.h index af824e5a97..16e79687d3 100644 --- a/repos/os/include/vfs/dir_file_system.h +++ b/repos/os/include/vfs/dir_file_system.h @@ -34,23 +34,22 @@ class Vfs::Dir_file_system : public File_system struct Dir_vfs_handle : Vfs_handle { - struct Sync_dir_handle_element; + struct Subdir_handle_element; - typedef Genode::Registry Sync_dir_handle_registry; + typedef Genode::Registry Subdir_handle_registry; - struct Sync_dir_handle_element : Sync_dir_handle_registry::Element + struct Subdir_handle_element : Subdir_handle_registry::Element { Vfs_handle &vfs_handle; - Sync_dir_handle_element(Sync_dir_handle_registry ®istry, - Vfs_handle &vfs_handle) - : Sync_dir_handle_registry::Element(registry, *this), + Subdir_handle_element(Subdir_handle_registry ®istry, + Vfs_handle &vfs_handle) + : Subdir_handle_registry::Element(registry, *this), vfs_handle(vfs_handle) { } }; Absolute_path path; - File_system *fs_for_complete_read { nullptr }; - Vfs_handle *fs_dir_handle { nullptr }; - Sync_dir_handle_registry sync_dir_handle_registry; + Vfs_handle *queued_read_handle { nullptr }; + Subdir_handle_registry subdir_handle_registry; Dir_vfs_handle(Directory_service &ds, File_io_service &fs, @@ -58,6 +57,16 @@ class Vfs::Dir_file_system : public File_system char const *path) : Vfs_handle(ds, fs, alloc, 0), path(path) { } + + ~Dir_vfs_handle() + { + /* close all sub-handles */ + auto f = [&] (Subdir_handle_element &e) { + e.vfs_handle.ds().close(&e.vfs_handle); + destroy(alloc(), &e); + }; + subdir_handle_registry.for_each(f); + } }; /* pointer to first child file system */ @@ -197,6 +206,10 @@ class Vfs::Dir_file_system : public File_system bool _queue_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle) { + bool result = true; + + dir_vfs_handle->queued_read_handle = nullptr; + file_offset index = dir_vfs_handle->seek() / sizeof(Dirent); char const *sub_path = _sub_path(dir_vfs_handle->path.base()); @@ -204,60 +217,49 @@ class Vfs::Dir_file_system : public File_system if (strlen(sub_path) == 0) sub_path = "/"; + /* base of composite directory index */ int base = 0; - for (File_system *fs = _first_file_system; fs; fs = fs->next) { + + auto f = [&] (Dir_vfs_handle::Subdir_handle_element &handle_element) { + if (dir_vfs_handle->queued_read_handle) return; /* skip through */ + + Vfs_handle &vfs_handle = handle_element.vfs_handle; /* * Determine number of matching directory entries within * the current file system. */ - int const fs_num_dirent = fs->num_dirent(sub_path); + int const fs_num_dirent = vfs_handle.ds().num_dirent(sub_path); /* * Query directory entry if index lies with the file * system. */ if (index - base < fs_num_dirent) { + /* set this handle to be used for read completion */ + dir_vfs_handle->queued_read_handle = &vfs_handle; - dir_vfs_handle->fs_for_complete_read = fs; - - Opendir_result opendir_result = - fs->opendir(sub_path, false, - &dir_vfs_handle->fs_dir_handle, - dir_vfs_handle->alloc()); - - /* - * Errors of this kind can only be communicated by - * 'complete_read()' - */ - if (opendir_result != OPENDIR_OK) - return true; - - dir_vfs_handle->fs_dir_handle->context = - dir_vfs_handle->context; - + /* seek to file-system local index */ index = index - base; - dir_vfs_handle->fs_dir_handle->seek(index * sizeof(Dirent)); + vfs_handle.seek(index * sizeof(Dirent)); - bool result = fs->queue_read(dir_vfs_handle->fs_dir_handle, - sizeof(Dirent)); + /* forward the handle context */ + vfs_handle.context = dir_vfs_handle->context; - return result; + result = vfs_handle.fs().queue_read(&vfs_handle, sizeof(Dirent)); } + }; - /* adjust base index for next file system */ - base += fs_num_dirent; - } + dir_vfs_handle->subdir_handle_registry.for_each(f); - return true; + return result; } Read_result _complete_read_of_file_systems(Dir_vfs_handle *dir_vfs_handle, char *dst, file_size count, file_size &out_count) { - if (!dir_vfs_handle->fs_for_complete_read || - !dir_vfs_handle->fs_dir_handle) { + if (!dir_vfs_handle->queued_read_handle) { /* * no fs was found for the given index or @@ -275,16 +277,14 @@ class Vfs::Dir_file_system : public File_system return READ_OK; } - Read_result result = dir_vfs_handle->fs_for_complete_read-> - complete_read(dir_vfs_handle->fs_dir_handle, + Read_result result = dir_vfs_handle->queued_read_handle->fs(). + complete_read(dir_vfs_handle->queued_read_handle, dst, count, out_count); - if (result != READ_OK) + if (result == READ_QUEUED) return result; - dir_vfs_handle->fs_for_complete_read->close(dir_vfs_handle->fs_dir_handle); - dir_vfs_handle->fs_dir_handle = nullptr; - dir_vfs_handle->fs_for_complete_read = nullptr; + dir_vfs_handle->queued_read_handle = nullptr; return result; } @@ -537,16 +537,60 @@ class Vfs::Dir_file_system : public File_system return OPEN_ERR_UNACCESSIBLE; } + /** + * Call 'opendir()' on each file system and store handles in + * a registry. + */ + Opendir_result open_composite_dirs(char const *sub_path, + Dir_vfs_handle &dir_vfs_handle) + { + Opendir_result result = OPENDIR_OK; + try { + for (File_system *fs = _first_file_system; (fs && result == OPENDIR_OK); fs = fs->next) { + Vfs_handle *sub_dir_handle = nullptr; + + Opendir_result r = fs->opendir( + sub_path, false, &sub_dir_handle, dir_vfs_handle.alloc()); + + switch (r) { + case OPENDIR_OK: + break; + case OPENDIR_ERR_LOOKUP_FAILED: + continue; + default: + result = r; /* loop will break */ + continue; + } + + new (dir_vfs_handle.alloc()) + Dir_vfs_handle::Subdir_handle_element( + dir_vfs_handle.subdir_handle_registry, *sub_dir_handle); + } + } + catch (Genode::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; } + return result; + } + Opendir_result opendir(char const *path, bool create, Vfs_handle **out_handle, Allocator &alloc) override { + Opendir_result result = OPENDIR_OK; + /* path equals "/" (for reading the name of this directory) */ if (strcmp(path, "/") == 0) { if (create) return OPENDIR_ERR_PERMISSION_DENIED; - *out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc, - path); - return OPENDIR_OK; + Dir_vfs_handle *root_handle = new (alloc) + Dir_vfs_handle(*this, *this, alloc, path); + result = open_composite_dirs("/", *root_handle); + if (result == OPENDIR_OK) { + *out_handle = root_handle; + } else { + /* close the root handle and the rest will follow */ + close(root_handle); + } + return result; } char const *sub_path = _sub_path(path); @@ -562,7 +606,7 @@ class Vfs::Dir_file_system : public File_system fs.opendir(path, true, &tmp_handle, alloc); if (opendir_result == OPENDIR_OK) fs.close(tmp_handle); - return opendir_result; + return opendir_result; /* return from lambda */ }; Opendir_result opendir_result = @@ -575,10 +619,17 @@ class Vfs::Dir_file_system : public File_system return opendir_result; } - *out_handle = new (alloc) Dir_vfs_handle(*this, *this, alloc, - path); + Dir_vfs_handle *dir_vfs_handle = new (alloc) + Dir_vfs_handle(*this, *this, alloc, path); - return OPENDIR_OK; + result = open_composite_dirs(sub_path, *dir_vfs_handle); + if (result == OPENDIR_OK) { + *out_handle = dir_vfs_handle; + } else { + /* close the master handle and the rest will follow */ + close(dir_vfs_handle); + } + return result; } Openlink_result openlink(char const *path, bool create, @@ -757,54 +808,23 @@ class Vfs::Dir_file_system : public File_system bool queue_sync(Vfs_handle *vfs_handle) override { + bool result = true; + Dir_vfs_handle *dir_vfs_handle = static_cast(vfs_handle); - char const *sub_path = _sub_path(dir_vfs_handle->path.base()); + auto f = [&result, dir_vfs_handle] (Dir_vfs_handle::Subdir_handle_element &e) { + /* forward the handle context */ + e.vfs_handle.context = dir_vfs_handle->context; - if (strlen(sub_path) == 0) - sub_path = "/"; + if (!e.vfs_handle.fs().queue_sync(&e.vfs_handle)) { + result = false; + } + }; - /* - * Call 'opendir()' on each file system and, if successful, - * 'queue_sync()'. If one file system's 'queue_sync()' returns - * false, this function returns false. Any VFS handles returned by - * 'opendir()' are kept in a registry. - */ - for (File_system *fs = _first_file_system; fs; fs = fs->next) { + dir_vfs_handle->subdir_handle_registry.for_each(f); - bool fs_dir_already_open = false; - - dir_vfs_handle->sync_dir_handle_registry.for_each( - [&] (Dir_vfs_handle::Sync_dir_handle_element &sync_dir_handle_element) { - if (&sync_dir_handle_element.vfs_handle.fs() == fs) { - fs_dir_already_open = true; - return; - } - } - ); - - if (fs_dir_already_open) - continue; - - Vfs_handle *sync_dir_handle; - - if (fs->opendir(sub_path, false, &sync_dir_handle, - dir_vfs_handle->alloc()) != OPENDIR_OK) - continue; - - sync_dir_handle->context = dir_vfs_handle->context; - - new (dir_vfs_handle->alloc()) - Dir_vfs_handle::Sync_dir_handle_element( - dir_vfs_handle->sync_dir_handle_registry, - *sync_dir_handle); - - if (!sync_dir_handle->fs().queue_sync(sync_dir_handle)) - return false; - } - - return true; + return result; } Sync_result complete_sync(Vfs_handle *vfs_handle) override @@ -814,21 +834,13 @@ class Vfs::Dir_file_system : public File_system Dir_vfs_handle *dir_vfs_handle = static_cast(vfs_handle); - dir_vfs_handle->sync_dir_handle_registry.for_each( - [&] (Dir_vfs_handle::Sync_dir_handle_element &sync_dir_handle_element) { + auto f = [&result, dir_vfs_handle] (Dir_vfs_handle::Subdir_handle_element &e) { + Sync_result r = e.vfs_handle.fs().complete_sync(&e.vfs_handle); + if (r != SYNC_OK) + result = r; + }; - Vfs_handle *vfs_handle = &sync_dir_handle_element.vfs_handle; - - result = - vfs_handle->fs().complete_sync(vfs_handle); - - if (result != SYNC_OK) - return; - - vfs_handle->ds().close(vfs_handle); - destroy(vfs_handle->alloc(), &sync_dir_handle_element); - } - ); + dir_vfs_handle->subdir_handle_registry.for_each(f); return result; }