mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-22 17:46:08 -04:00
fix(fs): prevent stack exhaustion in save mirroring recursion
- Moved the copy buffer to the heap via std::make_unique. - Added explicit null-safety and system shutdown checks to mirroring logic. - Hardened recursion guards during directory traversal to ensure stability. Signed-off-by: Collecting <collecting@noreply.localhost>
This commit is contained in:
@@ -60,18 +60,22 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
|||||||
|
|
||||||
void BufferedVfsCopy(VirtualFile source, VirtualFile dest) {
|
void BufferedVfsCopy(VirtualFile source, VirtualFile dest) {
|
||||||
if (!source || !dest) return;
|
if (!source || !dest) return;
|
||||||
|
const size_t source_size = source->GetSize();
|
||||||
|
if (source_size == 0) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
std::vector<u8> buffer(0x100000); // 1MB buffer
|
// Move buffer to heap to prevent stack exhaustion during deep recursion
|
||||||
|
auto buffer = std::make_unique<std::vector<u8>>(0x100000); // 1MB
|
||||||
dest->Resize(0);
|
dest->Resize(0);
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
while (offset < source->GetSize()) {
|
while (offset < source_size) {
|
||||||
const size_t to_read = std::min(buffer.size(), source->GetSize() - offset);
|
const size_t to_read = std::min(buffer->size(), source_size - offset);
|
||||||
source->Read(buffer.data(), to_read, offset);
|
source->Read(buffer->data(), to_read, offset);
|
||||||
dest->Write(buffer.data(), to_read, offset);
|
dest->Write(buffer->data(), to_read, offset);
|
||||||
offset += to_read;
|
offset += to_read;
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
LOG_ERROR(Service_FS, "Critical error during VFS mirror operation.");
|
LOG_ERROR(Service_FS, "Mirroring: Buffer copy failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,17 +270,16 @@ VirtualDir SaveDataFactory::GetMirrorDirectory(u64 title_id) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SaveDataFactory::SmartSyncFromSource(VirtualDir source, VirtualDir dest) const {
|
void SaveDataFactory::SmartSyncFromSource(VirtualDir source, VirtualDir dest) const {
|
||||||
// Citron: Shutdown and null safety
|
|
||||||
if (!source || !dest || system.IsShuttingDown()) {
|
if (!source || !dest || system.IsShuttingDown()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync files from Source to Destination
|
// Sync files
|
||||||
for (const auto& s_file : source->GetFiles()) {
|
const auto files = source->GetFiles();
|
||||||
|
for (const auto& s_file : files) {
|
||||||
if (!s_file) continue;
|
if (!s_file) continue;
|
||||||
std::string name = s_file->GetName();
|
const std::string name = s_file->GetName();
|
||||||
|
|
||||||
// Skip metadata and lock files
|
|
||||||
if (name == ".lock" || name == ".citron_save_size" || name.find("mirror_backup") != std::string::npos) {
|
if (name == ".lock" || name == ".citron_save_size" || name.find("mirror_backup") != std::string::npos) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -287,16 +290,18 @@ void SaveDataFactory::SmartSyncFromSource(VirtualDir source, VirtualDir dest) co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recurse into subdirectories
|
// Sync subdirectories
|
||||||
for (const auto& s_subdir : source->GetSubdirectories()) {
|
const auto subdirs = source->GetSubdirectories();
|
||||||
|
for (const auto& s_subdir : subdirs) {
|
||||||
if (!s_subdir) continue;
|
if (!s_subdir) continue;
|
||||||
|
const std::string sub_name = s_subdir->GetName();
|
||||||
|
|
||||||
// Prevent recursion into title-id-named folders to avoid infinite loops
|
// Recursion guard for title-id folders
|
||||||
if (s_subdir->GetName().find("0100") != std::string::npos) continue;
|
if (sub_name.find("0100") != std::string::npos) continue;
|
||||||
|
|
||||||
auto d_subdir = dest->GetDirectoryRelative(s_subdir->GetName());
|
auto d_subdir = dest->GetDirectoryRelative(sub_name);
|
||||||
if (!d_subdir) {
|
if (!d_subdir) {
|
||||||
d_subdir = dest->CreateDirectoryRelative(s_subdir->GetName());
|
d_subdir = dest->CreateDirectoryRelative(sub_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d_subdir) {
|
if (d_subdir) {
|
||||||
|
|||||||
Reference in New Issue
Block a user