From f7ba777fff6fbd519bb9016f7e2a26e7b5134471 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Mon, 12 Mar 2018 19:29:58 +0100 Subject: [PATCH] VFS audit plugin Plugin for auditing VFS access using the VFS server. Useful for tracking which files ported software expects to be present. Fix #2160 --- repos/gems/lib/mk/vfs_audit.mk | 5 + repos/gems/run/libc_vfs_audit.run | 80 ++++++ repos/gems/run/noux_vfs_audit.run | 74 +++++ repos/gems/src/lib/vfs/audit/target.mk | 2 + repos/gems/src/lib/vfs/audit/vfs_audit.cc | 311 ++++++++++++++++++++++ 5 files changed, 472 insertions(+) create mode 100644 repos/gems/lib/mk/vfs_audit.mk create mode 100644 repos/gems/run/libc_vfs_audit.run create mode 100644 repos/gems/run/noux_vfs_audit.run create mode 100644 repos/gems/src/lib/vfs/audit/target.mk create mode 100644 repos/gems/src/lib/vfs/audit/vfs_audit.cc diff --git a/repos/gems/lib/mk/vfs_audit.mk b/repos/gems/lib/mk/vfs_audit.mk new file mode 100644 index 000000000..50c27ff86 --- /dev/null +++ b/repos/gems/lib/mk/vfs_audit.mk @@ -0,0 +1,5 @@ +SRC_CC = vfs_audit.cc + +vpath %.cc $(REP_DIR)/src/lib/vfs/audit + +SHARED_LIB = yes diff --git a/repos/gems/run/libc_vfs_audit.run b/repos/gems/run/libc_vfs_audit.run new file mode 100644 index 000000000..ca4a67196 --- /dev/null +++ b/repos/gems/run/libc_vfs_audit.run @@ -0,0 +1,80 @@ +# +# \brief Test for auditing the vfs +# \author Emery Hemingway +# \date 2018-03-22 +# + +# +# Build +# + +build { core init server/vfs test/libc_vfs lib/vfs/audit } + +create_boot_directory + +# +# Generate config +# + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + } +append_if [have_include "power_on/qemu"] config { + } +append config { + + + + + + + + +} + +install_config $config + +# +# Boot modules +# + +build_boot_image { + core init vfs + ld.lib.so libc.lib.so + test-libc_vfs + vfs_audit.lib.so +} + +# +# Execute test case +# + +append qemu_args " -nographic " +run_genode_until {.*child "test-libc_vfs" exited with exit value 0.*} 60 + +# vi: set ft=tcl : diff --git a/repos/gems/run/noux_vfs_audit.run b/repos/gems/run/noux_vfs_audit.run new file mode 100644 index 000000000..9d51005de --- /dev/null +++ b/repos/gems/run/noux_vfs_audit.run @@ -0,0 +1,74 @@ +build { + core init + app/sequence + drivers/timer + lib/libc_noux + noux/minimal + noux-pkg/coreutils + server/vfs + lib/vfs/audit +} + +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + +build_boot_image { + core init ld.lib.so + coreutils.tar + libc_noux.lib.so + libm.lib.so + noux libc.lib.so + posix.lib.so + sequence + timer + vfs_audit.lib.so + vfs +} + +append qemu_args " -nographic -serial mon:stdio " + +# coreutils.tar is really huge when built for x86_64 + +run_genode_until {child "sequence" exited with exit value 0.*\n} 30 diff --git a/repos/gems/src/lib/vfs/audit/target.mk b/repos/gems/src/lib/vfs/audit/target.mk new file mode 100644 index 000000000..0a6334bd9 --- /dev/null +++ b/repos/gems/src/lib/vfs/audit/target.mk @@ -0,0 +1,2 @@ +TARGET = dummy-vfs_audit +LIBS = vfs_audit diff --git a/repos/gems/src/lib/vfs/audit/vfs_audit.cc b/repos/gems/src/lib/vfs/audit/vfs_audit.cc new file mode 100644 index 000000000..5a755ca5e --- /dev/null +++ b/repos/gems/src/lib/vfs/audit/vfs_audit.cc @@ -0,0 +1,311 @@ +/* + * \brief VFS audit plugin + * \author Emery Hemingway + * \date 2018-03-12 + */ + +/* + * Copyright (C) 2018 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. + */ + +#include +#include +#include + +namespace Vfs_audit { + using namespace Vfs; + class File_system; +} + +class Vfs_audit::File_system : public Vfs::File_system +{ + private: + + class Log : public Genode::Output + { + private: + + enum { BUF_SIZE = Genode::Log_session::MAX_STRING_LEN }; + + Genode::Log_connection _log; + + char _buf[BUF_SIZE]; + unsigned _num_chars = 0; + + void _flush() + { + _buf[_num_chars] = '\0'; + _log.write(Genode::Log_session::String(_buf, _num_chars+1)); + _num_chars = 0; + } + + public: + + Log(Genode::Env &env, char const *label) + : _log(env, label) { } + + void out_char(char c) override + { + _buf[_num_chars++] = c; + if (_num_chars >= sizeof(_buf)-1) + _flush(); + } + + template + void log(ARGS &&... args) + { + Output::out_args(*this, args...); + _flush(); + } + + } _audit_log; + + template + inline void _log(ARGS &&... args) { _audit_log.log(args...); } + + Vfs::File_system &_root_dir; + + Absolute_path const _audit_path; + + /** + * Expand a path to lay within the audit path + */ + inline Absolute_path _expand(char const *path) { + return Absolute_path(path+1, _audit_path.string()); } + + struct Handle final : Vfs_handle + { + Handle(Handle const &); + Handle &operator = (Handle const &); + + Absolute_path const path; + Vfs_handle *audit = nullptr; + + void sync_state() + { + audit->seek(Vfs_handle::seek()); + audit->context = context; + } + + Handle(Vfs_audit::File_system &fs, + Genode::Allocator &alloc, + int flags, + char const *path) + : Vfs_handle(fs, fs, alloc, flags), path(path) { }; + }; + + public: + + File_system(Genode::Env &env, + Genode::Allocator &alloc, + Genode::Xml_node config, + Vfs::Io_response_handler &io_handler, + Vfs::File_system &root_dir) + : + _audit_log(env, config.attribute_value("label", Genode::String<64>("audit")).string()), + _root_dir(root_dir), + _audit_path(config.attribute_value( + "path", Genode::String()).string()) + { + (void)env; + (void)alloc; + (void)io_handler; + } + + const char* type() override { return "audit"; } + + /*********************** + ** Directory service ** + ***********************/ + + Genode::Dataspace_capability dataspace(const char *path) override + { + _log(__func__, " ", path); + return _root_dir.dataspace(_expand(path).string()); + } + + void release(char const *path, Dataspace_capability ds) override + { + _log(__func__, " ", path); + return _root_dir.release(_expand(path).string(), ds); + } + + Open_result open(const char *path, unsigned int mode, Vfs::Vfs_handle **out, Genode::Allocator &alloc) override + { + _log(__func__, " ", path, " ", Genode::Hex(mode, Genode::Hex::OMIT_PREFIX, Genode::Hex::PAD)); + + Handle *local_handle; + try { local_handle = new (alloc) Handle(*this, alloc, mode, path); } + catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; } + + Open_result r = _root_dir.open( + _expand(path).string(), mode, &local_handle->audit, alloc); + + if (r == OPEN_OK) + *out = local_handle; + else + destroy(alloc, local_handle); + return r; + } + + Opendir_result opendir(char const *path, bool create, + Vfs_handle **out, Allocator &alloc) override + { + _log(__func__, " ", path, create ? " create " : ""); + + Handle *local_handle; + try { local_handle = new (alloc) Handle(*this, alloc, 0, path); } + catch (Genode::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; } + catch (Genode::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; } + + Opendir_result r = _root_dir.opendir( + _expand(path).string(), create, &local_handle->audit, alloc); + + if (r == OPENDIR_OK) + *out = local_handle; + else + destroy(alloc, local_handle); + return r; + } + + void close(Vfs::Vfs_handle *vfs_handle) override + { + Handle *h = static_cast(vfs_handle); + _log(__func__, " ", h->path); + if (h) { + h->audit->ds().close(h->audit); + destroy(h->alloc(), h); + } + } + + Stat_result stat(const char *path, Vfs::Directory_service::Stat &buf) override + { + _log(__func__, " ", path); + return _root_dir.stat(_expand(path).string(), buf); + } + + Unlink_result unlink(const char *path) override + { + _log(__func__, " ", path); + return _root_dir.unlink(_expand(path).string()); + } + + Rename_result rename(const char *from , const char *to) override + { + _log(__func__, " ", from, " ", to); + return _root_dir.rename(_expand(from).string(), _expand(to).string()); + } + + file_size num_dirent(const char *path) override + { + return _root_dir.num_dirent(_expand(path).string()); + } + + bool directory(char const *path) override + { + return _root_dir.directory(_expand(path).string()); + } + + const char* leaf_path(const char *path) override + { + return _root_dir.leaf_path(_expand(path).string()); + } + + /********************** + ** File I/O service ** + **********************/ + + Write_result write(Vfs_handle *vfs_handle, + const char *buf, file_size len, + file_size &out) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().write(h.audit, buf, len, out); + } + + bool queue_read(Vfs_handle *vfs_handle, file_size len) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().queue_read(h.audit, len); + } + + Read_result complete_read(Vfs_handle *vfs_handle, + char *buf, file_size len, + file_size &out) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().complete_read(h.audit, buf, len, out); + } + + bool read_ready(Vfs_handle *vfs_handle) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().read_ready(h.audit); + } + + bool notify_read_ready(Vfs_handle *vfs_handle) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().notify_read_ready(h.audit); + } + + Ftruncate_result ftruncate(Vfs_handle *vfs_handle, + file_size len) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + _log(__func__, " ", h.path, " ", len); + return h.audit->fs().ftruncate(h.audit, len); + } + + bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().check_unblock(h.audit, rd, wr, ex); + } + + void register_read_ready_sigh(Vfs_handle *vfs_handle, Signal_context_capability sigh) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + return h.audit->fs().register_read_ready_sigh(h.audit, sigh); + } + + Sync_result complete_sync(Vfs_handle *vfs_handle) override + { + Handle &h = *static_cast(vfs_handle); + h.sync_state(); + _log("sync ", h.path); + return h.audit->fs().complete_sync(h.audit); + } +}; + + +extern "C" Vfs::File_system_factory *vfs_file_system_factory(void) +{ + struct Factory : Vfs::File_system_factory + { + Vfs::File_system *create(Genode::Env &env, + Genode::Allocator &alloc, + Genode::Xml_node config, + Vfs::Io_response_handler &io_handler, + Vfs::File_system &root_dir) override + { + return new (alloc) + Vfs_audit::File_system(env, alloc, config, io_handler, root_dir); + } + }; + + static Factory f; + return &f; +}