Skip to content

Commit

Permalink
fix: support Mod Organizer 2.
Browse files Browse the repository at this point in the history
When launching with MO2, std::weakly_canonical is not behaving correctly. Security layer is now denying access when / or \ is present in child path, only with MO2.
Only use path from std::directory_iterator entries when listing files. Building a custom path was getting in the way when using MO2.
  • Loading branch information
poirierlouis committed Jun 2, 2024
1 parent e6ba9c8 commit a3a9cd5
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- support [MO2](https://github.com/ModOrganizer2/modorganizer/).

------------------------

Expand Down
2 changes: 1 addition & 1 deletion scripts/RedFileSystem/Test/FileSystemStorageTest.reds
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class FileSystemStorageTest extends BaseTest {
let status = this.m_storage.Exists("..\\..\\..\\..\\..\\..\\..\\steam.exe");
this.ExpectString("IsFile denied", s"\(status)", "Denied");
let status = this.m_storage.IsFile("test\\");
let status = this.m_storage.IsFile("test");
this.ExpectString("IsFile false", s"\(status)", "False");
let status = this.m_storage.IsFile("test.json");
Expand Down
18 changes: 16 additions & 2 deletions src/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ RED4ext::Logger* FileSystem::logger = nullptr;

std::filesystem::path FileSystem::game_path;
std::filesystem::path FileSystem::storages_path;
bool FileSystem::has_mo2 = false;

std::regex FileSystem::storage_name_rule("[A-Za-z]{3,24}");

Expand All @@ -20,6 +21,7 @@ void FileSystem::load(RED4ext::PluginHandle p_handle,
RED4ext::Logger* p_logger) {
handle = p_handle;
logger = p_logger;
detect_mo2();
auto path = std::filesystem::absolute(".");

game_path = path.parent_path().parent_path();
Expand Down Expand Up @@ -126,6 +128,10 @@ Red::Handle<FileSystemStorage> FileSystem::get_shared_storage() {
return storage;
}

bool FileSystem::is_mo2_detected() {
return has_mo2;
}

bool FileSystem::request_directory(const std::filesystem::path& p_path) {
std::error_code error;
bool is_present = std::filesystem::exists(p_path, error);
Expand All @@ -137,10 +143,10 @@ bool FileSystem::request_directory(const std::filesystem::path& p_path) {
return true;
}
is_present = std::filesystem::create_directory(p_path, error);
if (!is_present || error) {
if (error) {
return false;
}
return true;
return is_present;
}

bool FileSystem::migrate_directory(const std::filesystem::path& p_old_path,
Expand Down Expand Up @@ -169,6 +175,14 @@ bool FileSystem::migrate_directory(const std::filesystem::path& p_old_path,
return true;
}

void FileSystem::detect_mo2() {
has_mo2 = GetModuleHandle(TEXT("usvfs_x64.dll")) != nullptr;
if (!has_mo2) {
return;
}
logger->Info(handle, "RedFileSystem launched using MO2.");
}

Red::Handle<FileSystemStorage> FileSystem::find_storage(
const std::string& p_name) {
for (const auto& storage : storages) {
Expand Down
4 changes: 4 additions & 0 deletions src/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class FileSystem : public Red::IScriptable {

static std::filesystem::path game_path;
static std::filesystem::path storages_path;
static bool has_mo2;

static std::regex storage_name_rule;

Expand All @@ -33,6 +34,7 @@ class FileSystem : public Red::IScriptable {
static bool request_directory(const std::filesystem::path& p_path);
static bool migrate_directory(const std::filesystem::path& p_old_path,
const std::filesystem::path& p_new_path);
static void detect_mo2();

static Red::Handle<FileSystemStorage> find_storage(const std::string& p_name);

Expand All @@ -43,6 +45,8 @@ class FileSystem : public Red::IScriptable {
static Red::Handle<FileSystemStorage> get_storage(const Red::CString& p_name);
static Red::Handle<FileSystemStorage> get_shared_storage();

static bool is_mo2_detected();

RTTI_IMPL_TYPEINFO(RedFS::FileSystem);
RTTI_IMPL_ALLOCATOR();
};
Expand Down
21 changes: 16 additions & 5 deletions src/FileSystemStorage.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "FileSystemStorage.h"
#include "FileSystem.h"

#include <utility>

Expand Down Expand Up @@ -82,7 +83,7 @@ Red::DynArray<Red::Handle<File>> FileSystemStorage::get_files() {
for (const auto& entry : entries) {
if (entry.is_regular_file()) {
auto file_name = entry.path().filename();
auto file_path = storage_path / file_name;
auto file_path = entry.path();
auto file_mutex = get_mutex(file_path);
auto file = Red::MakeHandle<File>(file_mutex, file_name, file_path);

Expand Down Expand Up @@ -123,7 +124,7 @@ Red::DynArray<Red::Handle<AsyncFile>> FileSystemStorage::get_async_files() {
for (const auto& entry : entries) {
if (entry.is_regular_file()) {
auto file_name = entry.path().filename();
auto file_path = storage_path / file_name;
auto file_path = entry.path();
auto file_mutex = get_mutex(file_path);
auto file = Red::MakeHandle<AsyncFile>(file_mutex, file_name, file_path);

Expand All @@ -136,9 +137,19 @@ Red::DynArray<Red::Handle<AsyncFile>> FileSystemStorage::get_async_files() {
std::filesystem::path FileSystemStorage::restrict_path(
const std::string& p_path, std::error_code& p_error) const {
std::filesystem::path path = storage_path / p_path;
std::filesystem::path real_path =
std::filesystem::weakly_canonical(path, p_error);

std::filesystem::path real_path;

// NOTE: See issue regarding usage of `std::weakly_canonical` with MO2:
// https://github.com/ModOrganizer2/modorganizer/issues/2039
if (FileSystem::is_mo2_detected()) {
if (p_path.find('/') != std::string::npos ||
p_path.find('\\') != std::string::npos) {
p_error = std::make_error_code(std::errc::permission_denied);
}
return path;
} else {
real_path = std::filesystem::weakly_canonical(path, p_error);
}
if (p_error) {
return real_path;
}
Expand Down

0 comments on commit a3a9cd5

Please sign in to comment.