mirror of
https://github.com/mikage-emu/mikage-dev.git
synced 2025-01-22 21:41:08 +01:00
Add fs_common.cpp
This commit is contained in:
parent
c7a6ea0846
commit
28f5973e85
1 changed files with 136 additions and 0 deletions
136
source/processes/fs_common.cpp
Normal file
136
source/processes/fs_common.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
#include "fs_common.hpp"
|
||||
#include "pxi.hpp"
|
||||
|
||||
#include <range/v3/algorithm/find.hpp>
|
||||
#include <range/v3/algorithm/generate_n.hpp>
|
||||
#include <range/v3/iterator/insert_iterators.hpp>
|
||||
|
||||
namespace HLE {
|
||||
|
||||
CommonPath CommonPath::FromUtf16(std::u16string_view utf16_data) {
|
||||
while (!utf16_data.empty() && utf16_data.back() == 0) {
|
||||
utf16_data.remove_suffix(1);
|
||||
}
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> conversion;
|
||||
return { Utf8PathType { conversion.to_bytes(utf16_data.begin(), utf16_data.end()) } };
|
||||
}
|
||||
|
||||
CommonPath CommonPath::FromUtf16(std::basic_string_view<uint16_t> utf16_data) {
|
||||
while (!utf16_data.empty() && utf16_data.back() == 0) {
|
||||
utf16_data.remove_suffix(1);
|
||||
}
|
||||
std::vector<char16_t> char16_data(utf16_data.size());
|
||||
std::copy(utf16_data.begin(), utf16_data.end(), char16_data.begin());
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> conversion;
|
||||
return { Utf8PathType { conversion.to_bytes(&*char16_data.begin(), &*char16_data.end()) } };
|
||||
}
|
||||
|
||||
CommonPath::CommonPath(OS::Thread& thread, uint32_t type, uint32_t num_bytes, const IPC::StaticBuffer& path)
|
||||
: CommonPath(type, num_bytes, [&,offset=0]() mutable { return thread.ReadMemory(path.addr + offset++); }) {
|
||||
}
|
||||
|
||||
CommonPath::CommonPath(OS::Thread& thread, uint32_t type, uint32_t num_bytes, const PXI::PXIBuffer& path)
|
||||
: CommonPath(type, num_bytes, [&,offset=0]() mutable { return path.Read<uint8_t>(thread, offset++); }) {
|
||||
}
|
||||
|
||||
template<typename ReadByte>
|
||||
CommonPath::CommonPath(uint32_t type, uint32_t num_bytes, ReadByte&& read) {
|
||||
switch (type) {
|
||||
case 1: // empty path
|
||||
{
|
||||
data = Utf8PathType { };
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: // binary path
|
||||
{
|
||||
data = BinaryPathType { };
|
||||
auto& path_data = std::get<BinaryPathType>(data);
|
||||
|
||||
if (num_bytes > max_length) {
|
||||
throw std::runtime_error(fmt::format("Path length {} exceeded maximum {}", num_bytes, path_data.capacity()));
|
||||
}
|
||||
|
||||
ranges::generate_n(ranges::back_inserter(path_data), num_bytes, read);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: // ASCII path
|
||||
{
|
||||
data = Utf8PathType { };
|
||||
auto& path = std::get<Utf8PathType>(data);
|
||||
|
||||
path.reserve(num_bytes);
|
||||
ranges::generate_n(ranges::back_inserter(path), num_bytes, read);
|
||||
|
||||
// Strip characters past the null terminator
|
||||
path.erase(ranges::find(path, '\0'), path.end());
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: // UTF-16 path
|
||||
{
|
||||
if (num_bytes % 2)
|
||||
throw std::runtime_error("Expected path size for UTF16 to be a multiple of 2");
|
||||
|
||||
// Read in data as UTF-16 with host byte order
|
||||
std::u16string utf16_data;
|
||||
utf16_data.reserve(num_bytes / 2);
|
||||
auto ReadFromAddr = [&]() -> char16_t {
|
||||
uint16_t low = read();
|
||||
uint32_t high = read();
|
||||
return low | (high << uint16_t { 8 });
|
||||
};
|
||||
ranges::generate_n(ranges::back_inserter(utf16_data), num_bytes / 2, ReadFromAddr);
|
||||
|
||||
// Strip characters past the null terminator
|
||||
utf16_data.erase(ranges::find(utf16_data, u'\0'), utf16_data.end());
|
||||
|
||||
*this = FromUtf16(utf16_data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error(fmt::format("Unknown path type {}", type));
|
||||
}
|
||||
}
|
||||
|
||||
ValidatedHostPath PathValidator::ValidateAndGetSandboxedTreePath(const Utf8PathType& utf8_path) const {
|
||||
auto path = utf8_path.to_std_path().lexically_normal();
|
||||
auto path_elements = std::distance(path.begin(), path.end());
|
||||
if (path_elements == 0 || *path.begin() != "/") {
|
||||
test_mode ? throw IPC::IPCError { 0, 0xe0e046be } // TODO: Throw PathValidator errors instead
|
||||
: throw std::runtime_error("Path does not start with '/'");
|
||||
}
|
||||
|
||||
if (path_elements > 1 && *std::next(path.begin()) == "..") {
|
||||
test_mode ? throw IPC::IPCError { 0, 0xe0e046be }
|
||||
: throw std::runtime_error("Path escapes root directory");
|
||||
}
|
||||
|
||||
// const uint32_t max_filename_length = 0x10; // TODO: Where does this come from? Seems overly short for HostArchives
|
||||
const uint32_t max_filename_length = 0x100;
|
||||
if (std::prev(path.end())->u8string().size() > max_filename_length) {
|
||||
test_mode ? throw IPC::IPCError { 0, 0xe0e046c7 }
|
||||
: throw std::runtime_error("Filename too long");
|
||||
}
|
||||
|
||||
// Append path to the sandbox root and double-check that we won't leak out of it
|
||||
auto sandbox_root = GetSandboxHostRoot();
|
||||
if (!sandbox_root.is_absolute()) {
|
||||
// TODO: Required for cfg
|
||||
// throw std::runtime_error("Sandbox root is not an absolute path: " + sandbox_root.string());
|
||||
}
|
||||
sandbox_root = canonical(sandbox_root);
|
||||
|
||||
auto new_path = sandbox_root;
|
||||
new_path += utf8_path;
|
||||
new_path = new_path.lexically_normal();
|
||||
if (new_path.begin() != std::search(new_path.begin(), new_path.end(), sandbox_root.begin(), sandbox_root.end())) {
|
||||
throw std::runtime_error(fmt::format("Requested path leaked out of sandbox: Got path {}, expected a child of {}", new_path, sandbox_root));
|
||||
}
|
||||
return ValidatedHostPath { std::move(new_path) };
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue