replaced the virtual file system back by the original custom one

This commit is contained in:
faraphel 2024-08-22 19:39:32 +02:00
parent d9ae04886f
commit 64c247ba6f
22 changed files with 373 additions and 31 deletions

3
.gitmodules vendored
View file

@ -7,6 +7,3 @@
[submodule "external/wit"]
path = external/wit
url = https://git.faraphel.fr/Atlas/library-wit
[submodule "external/vfspp"]
path = external/vfspp
url = https://github.com/nextgeniuspro/vfspp

View file

@ -23,28 +23,34 @@ add_executable(Atlas-Launcher
# Source
source/main.cpp
source/script/module/debug/load.cpp
source/script/module/debug/load.hpp
source/script/module/debug/_load.cpp
source/script/module/debug/_load.hpp
source/script/module/debug/log.cpp
source/script/module/debug/log.hpp
source/script/engine/Engine.cpp
source/script/engine/Engine.hpp
source/script/module/filesystem/_load.cpp
source/script/module/filesystem/_load.hpp
source/script/module/filesystem/Path.cpp
source/script/module/filesystem/Path.hpp
)
target_include_directories(Atlas-Launcher PRIVATE
source/
# AngelScript
external/angelscript/add_on
# Source
source/
)
target_link_libraries(Atlas-Launcher PRIVATE
# Libraries
angelscript
vfspp
# Tools
# SZS
VFS
)
# Copy the mods
file(COPY .mods DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
add_subdirectory(external/angelscript/angelscript/projects/cmake)
add_subdirectory(external/vfspp)
add_subdirectory(external/vfs)
# add_subdirectory(external/szs)

18
external/vfs/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.28)
project(VFS LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(VFS STATIC
source/VirtualFileSystem.cpp
source/exception/FileNotFoundException.cpp
include/exception/FileNotFoundException.hpp
)
target_include_directories(VFS PUBLIC
include
)
add_subdirectory(tests)

10
external/vfs/README.md vendored Normal file
View file

@ -0,0 +1,10 @@
# Restricted Virtual File System
This is a micro-library used to create a virtual file system to restrict access
to the host file system while still being able to mount directories that can be accessed.
As of right now, this is an incredibly simple implementation of this system that
work by mapping virtual directory path to a corresponding real directory path, replacing
them while resolving the path and reject all others paths that have not been mounted
to avoid access to other files.
It does not emulate a real file system nor allow control over permissions of the files.

View file

@ -0,0 +1,49 @@
#pragma once
#include <filesystem>
namespace vfs {
/**
* Represent a virtual file system.
* Real directory can be mounted to safely interact with the real file system.
*/
class VirtualFileSystem {
public:
explicit VirtualFileSystem();
~VirtualFileSystem();
std::filesystem::path getWorkingDirectory() const;
void setWorkingDirectory(const std::filesystem::path& virtualPath);
/**
* Mount a real directory into the virtual file system
* @param sourcePath the path to the directory in the real file system
* @param virtualDestinationPath the path to the directory in the virtual file system
*/
void mount(const std::filesystem::path& sourcePath, const std::filesystem::path& virtualDestinationPath) const;
/**
* Unmount a directory from the virtual file system
* @param virtualPath the path to the directory in the virtual file system
*/
void unmount(const std::filesystem::path& virtualPath) const;
/**
* Get the real path on the host file system from its virtual path
* @param virtualPath the virtual path
* @return the real path
*/
std::filesystem::path resolve(const std::filesystem::path& virtualPath) const;
private:
/// the internal temporary directory
std::filesystem::path rootDirectoryPath;
/// the virtual current working directory. Always relative.
std::filesystem::path virtualWorkingDirectory;
};
}

View file

@ -0,0 +1,14 @@
#pragma once
#include <stdexcept>
#include <filesystem>
namespace vfs::exception {
class FileNotFoundException final : public std::runtime_error {
public:
explicit FileNotFoundException(const std::filesystem::path& path);
};
}

View file

@ -0,0 +1,88 @@
#include "VirtualFileSystem.hpp"
#include <iostream>
#include "exception/FileNotFoundException.hpp"
namespace vfs {
VirtualFileSystem::VirtualFileSystem() {
// create the temporary directory
char temporaryDirectoryPathTemplate[] = "/tmp/vfs-XXXXXX";
const char* temporaryDirectoryPath = mkdtemp(temporaryDirectoryPathTemplate);
// check for any issue while creating the directory
if (temporaryDirectoryPath == nullptr)
throw std::runtime_error("Could not create a temporary directory for the virtual file system.");
// store it as a standard path
this->rootDirectoryPath = std::filesystem::path(temporaryDirectoryPath);
// set the virtual working directory at the root
this->setWorkingDirectory("./");
}
VirtualFileSystem::~VirtualFileSystem() {
// delete everything in the temporary directory
std::error_code error;
remove_all(this->rootDirectoryPath, error);
// check for any error
if (error)
std::cerr << "Could not clean the VFS : " + error.message() << std::endl;
}
std::filesystem::path VirtualFileSystem::getWorkingDirectory() const {
return this->virtualWorkingDirectory;
}
void VirtualFileSystem::setWorkingDirectory(const std::filesystem::path& virtualPath) {
if (!virtualPath.is_relative())
throw std::runtime_error("The working directory need to be a relative path (for the virtual file system root).");
// set the new working directory
this->virtualWorkingDirectory = virtualPath;
}
void VirtualFileSystem::mount(const std::filesystem::path& sourcePath, const std::filesystem::path& virtualDestinationPath) const {
// create a symlink from the directory to our temporary directory
const std::filesystem::path realDestinationPath = this->resolve(virtualDestinationPath);
// create a symlink to the directory to mount in our virtual file system directory
std::error_code error;
create_directory_symlink(absolute(sourcePath), realDestinationPath, error);
// check for any error while creating the symlink
if (error)
throw std::runtime_error("Could not mount the directory in the virtual file system. Reason: " + error.message());
}
void VirtualFileSystem::unmount(const std::filesystem::path& virtualPath) const {
// get the real path of the given virtual path
const std::filesystem::path realPath = this->resolve(virtualPath);
// check if it is a symbolic link
if (!is_symlink(realPath))
throw std::runtime_error("Not a mounted directory !");
// delete the symlink
remove(realPath);
}
std::filesystem::path VirtualFileSystem::resolve(const std::filesystem::path& virtualPath) const {
// normalize the path by solving the special symbols first.
std::filesystem::path normalizedVirtualPath = virtualPath.lexically_normal();
if (virtualPath.is_relative()) {
// if the path is relative, prepend the working directory
normalizedVirtualPath = this->virtualWorkingDirectory / normalizedVirtualPath;
} else {
// convert it into a relative path for the root
normalizedVirtualPath = relative(normalizedVirtualPath.lexically_normal(), "/");
}
// get the real path by prepending the real root directory path
return this->rootDirectoryPath / normalizedVirtualPath;
}
}

View file

@ -0,0 +1,9 @@
#include "exception/FileNotFoundException.hpp"
namespace vfs::exception {
FileNotFoundException::FileNotFoundException(const std::filesystem::path& path) :
std::runtime_error("Could not find the file : " + path.string()) {}
}

25
external/vfs/tests/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.28)
project(Test-VFS LANGUAGES CXX)
enable_testing()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Test executable
add_executable(Test-VFS
test-restriction.cpp
)
# Libraries
target_link_libraries(Test-VFS PRIVATE
# Code to test
VFS
)
# Copy the assets to run the tests
file(COPY _assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
# Test
add_test(NAME Test-VFS COMMAND Test-VFS)

View file

@ -0,0 +1 @@
This is a simple text file.

38
external/vfs/tests/test-restriction.cpp vendored Normal file
View file

@ -0,0 +1,38 @@
#include <filesystem>
#include <iostream>
#include <VirtualFileSystem.hpp>
int main() {
// TODO(Faraphel): use a real unit test framework ?
// create a basic virtual file system
vfs::VirtualFileSystem fs;
// mount a directory into our system
fs.mount("_assets", "/assets");
// TEST: check if a file outside of an allowed mounted directory can't be resolved
try {
const std::filesystem::path path = fs.resolve("/content.txt");
throw std::runtime_error("[Test 1] - Impossible path should not be resolved !");
} catch (const std::runtime_error& exception) {}
// TEST: check if a file in a mounted directory can be resolved
try {
const std::filesystem::path path = fs.resolve("/assets/content.txt");
// auto file = QFile(path);
// file.open(QIODeviceBase::Text | QIODevice::ReadOnly);
// std::cout << file.readAll().toStdString() << std::endl;
} catch (const std::runtime_error& exception) {
throw std::runtime_error("[Test 2] - Path could not be accessed !");
}
// TEST: cannot bypass security with the ".." parent directory syntax
try {
// const std::filesystem::path path = fs.resolve("/assets/../content.txt");
// throw std::runtime_error("[Test 3] - Resolved impossible path");
} catch (const std::runtime_error& exception) {
}
}

1
external/vfspp vendored

@ -1 +0,0 @@
Subproject commit f22a0b23827e0ff3faa8379dc0bad450db9ac17a

View file

@ -1,16 +1,16 @@
#include <iostream>
#include <fstream>
#include <memory>
#include <vfspp/VirtualFileSystem.hpp>
#include <vfspp/MemoryFileSystem.hpp>
#include <angelscript.h>
#include <VirtualFileSystem.hpp>
#include <scriptbuilder/scriptbuilder.h>
#include "script/engine/Engine.hpp"
#include "script/module/debug/load.hpp"
#include "script/module/debug/_load.hpp"
int main(int argc, char* argv[]) {
int main_AngelScript(int argc, char* argv[]) {
int error;
// engine
@ -64,3 +64,29 @@ int main(int argc, char* argv[]) {
return error;
}
int main_vfspp(int argc, char* argv[]) {
int error;
// create a virtual file system
const auto vfs = std::make_unique<vfs::VirtualFileSystem>();
vfs->mount(".mods/Atlas/assets", "assets");
// get an example file
auto file = vfs->resolve("assets/message.txt");
// open and read it
auto stream = std::ifstream(file);
if (!stream.is_open())
throw std::runtime_error("Could not open the file.");
std::string line;
std::getline(stream, line);
std::cout << line;
return 0;
}
int main(int argc, char* argv[]) {
main_vfspp(argc, argv);
}

View file

@ -2,6 +2,7 @@
#include <angelscript.h>
#include <format>
#include <iostream>
#include <stdexcept>
#include <datetime/datetime.h>
#include <scriptany/scriptany.h>
@ -12,7 +13,6 @@
#include <scripthandle/scripthandle.h>
#include <scripthelper/scripthelper.h>
#include <scriptmath/scriptmath.h>
#include <vfspp/MemoryFileSystem.hpp>
#include <weakref/weakref.h>
@ -38,15 +38,6 @@ Engine::Engine() {
RegisterScriptWeakRef(this->asEngine);
RegisterExceptionRoutines(this->asEngine);
RegisterScriptMath(this->asEngine);
// initialise the file system
this->fileSystem = std::make_unique<vfspp::VirtualFileSystem>();
// TODO(Faraphel): is the memory fs
auto memoryFileSystem = std::make_unique<vfspp::MemoryFileSystem>();
memoryFileSystem->Initialize();
this->fileSystem->AddFileSystem("/tmp", std::move(memoryFileSystem));
}
void Engine::asCallback(const asSMessageInfo *message, void *args) {
@ -84,7 +75,7 @@ Engine::~Engine() {
this->asEngine->ShutDownAndRelease();
}
asIScriptEngine *Engine::getAsEngine() const {
asIScriptEngine* Engine::getAsEngine() const {
return this->asEngine;
}

View file

@ -1,7 +1,6 @@
#pragma once
#include <angelscript.h>
#include <vfspp/VirtualFileSystem.hpp>
namespace atlas::script::engine {
@ -27,9 +26,9 @@ public:
* @return the AngelScript engine
*/
[[nodiscard]] asIScriptEngine* getAsEngine() const;
private:
asIScriptEngine* asEngine;
std::unique_ptr<vfspp::VirtualFileSystem> fileSystem;
};
}

View file

@ -1,5 +1,6 @@
#include "load.hpp"
#include "_load.hpp"
#include <stdexcept>
#include <string>
#include "log.hpp"

View file

@ -5,6 +5,10 @@
namespace atlas::script::module::debug {
/**
* Load the debug features into an Atlas engine
* @param atlasEngine the Atlas engine
*/
void load(const engine::Engine* atlasEngine);
}

View file

@ -5,6 +5,12 @@
namespace atlas::script::module::debug {
/**
* @brief Log a message
* @details Display a message in the execution logs
* @param category the category of the message
* @param message the message
*/
void log(const std::string& category, const std::string& message);
}

View file

@ -0,0 +1,8 @@
#include "Path.hpp"
namespace atlas::script::module::filesystem {
Path::Path(const engine::Engine* atlasEngine, const std::string &path) {
}
}

View file

@ -0,0 +1,17 @@
#pragma once
#include <string>
#include "script/engine/Engine.hpp"
namespace atlas::script::module::filesystem {
/**
* Represent a path in the virtual file system
*/
class Path {
public:
explicit Path(const engine::Engine* atlasEngine, const std::string& path);
};
}

View file

@ -0,0 +1,23 @@
#include "_load.hpp"
#include <stdexcept>
namespace atlas::script::module::filesystem {
void load(const engine::Engine* atlasEngine) {
asIScriptEngine* asEngine = atlasEngine->getAsEngine();
int error;
// start namespace
error = asEngine->SetDefaultNamespace("atlas::filesystem");
if (error < 0)
throw std::runtime_error("Could not enter the \"atlas::filesystem\" namespace.");
// end namespace
error = asEngine->SetDefaultNamespace("");
if (error < 0)
throw std::runtime_error("Could not reset the namespace.");
}
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "script/engine/Engine.hpp"
namespace atlas::script::module::filesystem {
/**
* Load the file system features into an Atlas engine
* @param atlasEngine the Atlas engine
*/
void load(const engine::Engine* atlasEngine);
}