From 100583e262af7a175bf259235cb514930015bc82 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Tue, 18 May 2021 12:16:44 +0200 Subject: [PATCH] os/vfs.h: New_file utility This patch moves the utility from the app/text_area to os/vfs.h to make it easier to use by other components. By hosting the 'New_file' as a friend alongside the 'Directory', we can now pass a 'Directory' as constructor argument, which is consistent with other utilities such as 'File_content'. As a further improvement, the new version supports the implicit creation of the directory hierarchy leading to the new file. Issue #4032 --- repos/gems/src/app/text_area/main.cc | 1 - repos/gems/src/app/text_area/new_file.h | 147 ----------------------- repos/os/include/os/vfs.h | 148 +++++++++++++++++++++++- 3 files changed, 144 insertions(+), 152 deletions(-) delete mode 100644 repos/gems/src/app/text_area/new_file.h diff --git a/repos/gems/src/app/text_area/main.cc b/repos/gems/src/app/text_area/main.cc index 82b3bbaf67..4404fd9768 100644 --- a/repos/gems/src/app/text_area/main.cc +++ b/repos/gems/src/app/text_area/main.cc @@ -26,7 +26,6 @@ #include #include #include -#include #include namespace Text_area { struct Main; } diff --git a/repos/gems/src/app/text_area/new_file.h b/repos/gems/src/app/text_area/new_file.h deleted file mode 100644 index f34228763e..0000000000 --- a/repos/gems/src/app/text_area/new_file.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * \brief Utility for writing data to a file via the Genode VFS library - * \author Norman Feske - * \date 2020-01-25 - */ - -/* - * Copyright (C) 2020 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 _NEW_FILE_H_ -#define _NEW_FILE_H_ - -#include - -namespace Genode { class New_file; } - -class Genode::New_file : Noncopyable -{ - public: - - struct Create_failed : Exception { }; - - private: - - Entrypoint &_ep; - Allocator &_alloc; - Vfs::File_system &_fs; - Vfs::Vfs_handle &_handle; - - Vfs::Vfs_handle &_init_handle(Directory::Path const &path) - { - unsigned mode = Vfs::Directory_service::OPEN_MODE_WRONLY; - - Vfs::Directory_service::Stat stat { }; - if (_fs.stat(path.string(), stat) != Vfs::Directory_service::STAT_OK) - mode |= Vfs::Directory_service::OPEN_MODE_CREATE; - - Vfs::Vfs_handle *handle_ptr = nullptr; - Vfs::Directory_service::Open_result const res = - _fs.open(path.string(), mode, &handle_ptr, _alloc); - - if (res != Vfs::Directory_service::OPEN_OK || (handle_ptr == nullptr)) { - error("failed to create file '", path, "'"); - throw Create_failed(); - } - - handle_ptr->fs().ftruncate(handle_ptr, 0); - - return *handle_ptr; - } - - public: - - /** - * Constructor - * - * \throw Open_failed - */ - New_file(Vfs::Env &env, Directory::Path const &path) - : - _ep(env.env().ep()), _alloc(env.alloc()), _fs(env.root_dir()), - _handle(_init_handle(path)) - { } - - ~New_file() - { - while (_handle.fs().queue_sync(&_handle) == false) - _ep.wait_and_dispatch_one_io_signal(); - - for (bool sync_done = false; !sync_done; ) { - - switch (_handle.fs().complete_sync(&_handle)) { - - case Vfs::File_io_service::SYNC_QUEUED: - break; - - case Vfs::File_io_service::SYNC_ERR_INVALID: - warning("could not complete file sync operation"); - sync_done = true; - break; - - case Vfs::File_io_service::SYNC_OK: - sync_done = true; - break; - } - - if (!sync_done) - _ep.wait_and_dispatch_one_io_signal(); - } - _handle.ds().close(&_handle); - } - - enum class Append_result { OK, WRITE_ERROR }; - - Append_result append(char const *src, size_t size) - { - bool write_error = false; - - size_t remaining_bytes = size; - - while (remaining_bytes > 0 && !write_error) { - - bool stalled = false; - - try { - Vfs::file_size out_count = 0; - - using Write_result = Vfs::File_io_service::Write_result; - - switch (_handle.fs().write(&_handle, src, remaining_bytes, - out_count)) { - - case Write_result::WRITE_ERR_AGAIN: - case Write_result::WRITE_ERR_WOULD_BLOCK: - stalled = true; - break; - - case Write_result::WRITE_ERR_INVALID: - case Write_result::WRITE_ERR_IO: - case Write_result::WRITE_ERR_INTERRUPT: - write_error = true; - break; - - case Write_result::WRITE_OK: - out_count = min(remaining_bytes, out_count); - remaining_bytes -= out_count; - src += out_count; - _handle.advance_seek(out_count); - break; - }; - } - catch (Vfs::File_io_service::Insufficient_buffer) { - stalled = true; } - - if (stalled) - _ep.wait_and_dispatch_one_io_signal(); - } - return write_error ? Append_result::WRITE_ERROR - : Append_result::OK; - } -}; - -#endif /* _NEW_FILE_H_ */ diff --git a/repos/os/include/os/vfs.h b/repos/os/include/os/vfs.h index 987bf87d5f..9f8e7af3f9 100644 --- a/repos/os/include/os/vfs.h +++ b/repos/os/include/os/vfs.h @@ -25,11 +25,12 @@ namespace Genode { struct Directory; struct Root_directory; struct File; - struct Readonly_file; - struct File_content; - struct Watcher; + class Readonly_file; + class File_content; + class New_file; + class Watcher; template - struct Watch_handler; + class Watch_handler; } @@ -117,6 +118,7 @@ struct Genode::Directory : Noncopyable, Interface friend class Readonly_file; friend class Root_directory; friend class Watcher; + friend class New_file; /* * Operations such as 'file_size' that are expected to be 'const' at @@ -617,6 +619,144 @@ class Genode::File_content }; +/** + * Utility for writing data to a file via the Genode VFS library + */ +class Genode::New_file : Noncopyable +{ + public: + + struct Create_failed : Exception { }; + + private: + + Entrypoint &_ep; + Allocator &_alloc; + Vfs::File_system &_fs; + Vfs::Vfs_handle &_handle; + + Vfs::Vfs_handle &_init_handle(Directory &dir, Directory::Path const &rel_path) + { + /* create compound directory */ + { + Genode::Path dir_path { rel_path }; + dir_path.strip_last_element(); + dir.create_sub_directory(dir_path.string()); + } + + unsigned mode = Vfs::Directory_service::OPEN_MODE_WRONLY; + + Directory::Path const path = Directory::join(dir._path, rel_path); + + if (!dir.file_exists(path)) + mode |= Vfs::Directory_service::OPEN_MODE_CREATE; + + Vfs::Vfs_handle *handle_ptr = nullptr; + Vfs::Directory_service::Open_result const res = + _fs.open(path.string(), mode, &handle_ptr, _alloc); + + if (res != Vfs::Directory_service::OPEN_OK || (handle_ptr == nullptr)) { + error("failed to create file '", path, "', res=", (int)res); + throw Create_failed(); + } + + handle_ptr->fs().ftruncate(handle_ptr, 0); + + return *handle_ptr; + } + + public: + + /** + * Constructor + * + * \throw Create_failed + */ + New_file(Directory &dir, Directory::Path const &path) + : + _ep(dir._ep), _alloc(dir._alloc), _fs(dir._fs), + _handle(_init_handle(dir, path)) + { } + + ~New_file() + { + while (_handle.fs().queue_sync(&_handle) == false) + _ep.wait_and_dispatch_one_io_signal(); + + for (bool sync_done = false; !sync_done; ) { + + switch (_handle.fs().complete_sync(&_handle)) { + + case Vfs::File_io_service::SYNC_QUEUED: + break; + + case Vfs::File_io_service::SYNC_ERR_INVALID: + warning("could not complete file sync operation"); + sync_done = true; + break; + + case Vfs::File_io_service::SYNC_OK: + sync_done = true; + break; + } + + if (!sync_done) + _ep.wait_and_dispatch_one_io_signal(); + } + _handle.ds().close(&_handle); + } + + enum class Append_result { OK, WRITE_ERROR }; + + Append_result append(char const *src, size_t size) + { + bool write_error = false; + + size_t remaining_bytes = size; + + while (remaining_bytes > 0 && !write_error) { + + bool stalled = false; + + try { + Vfs::file_size out_count = 0; + + using Write_result = Vfs::File_io_service::Write_result; + + switch (_handle.fs().write(&_handle, src, remaining_bytes, + out_count)) { + + case Write_result::WRITE_ERR_AGAIN: + case Write_result::WRITE_ERR_WOULD_BLOCK: + stalled = true; + break; + + case Write_result::WRITE_ERR_INVALID: + case Write_result::WRITE_ERR_IO: + case Write_result::WRITE_ERR_INTERRUPT: + write_error = true; + break; + + case Write_result::WRITE_OK: + out_count = min(remaining_bytes, out_count); + remaining_bytes -= out_count; + src += out_count; + _handle.advance_seek(out_count); + break; + }; + } + catch (Vfs::File_io_service::Insufficient_buffer) { + stalled = true; } + + if (stalled) + _ep.wait_and_dispatch_one_io_signal(); + } + return write_error ? Append_result::WRITE_ERROR + : Append_result::OK; + } +}; + + class Genode::Watcher { private: