core/fs: fix file path when using separated update folder for *nix (#1403)

This commit is contained in:
Quang Ngô 2024-10-18 11:51:51 +07:00 committed by GitHub
parent 181b005636
commit ddc35639a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -28,16 +28,11 @@ void MntPoints::UnmountAll() {
m_mnt_pairs.clear(); m_mnt_pairs.clear();
} }
std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, bool* is_read_only) { std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) {
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf // Evil games like Turok2 pass double slashes e.g /app0//game.kpf
std::string corrected_path(guest_directory); const auto normalized_path = std::filesystem::path(path).lexically_normal().string();
size_t pos = corrected_path.find("//");
while (pos != std::string::npos) {
corrected_path.replace(pos, 2, "/");
pos = corrected_path.find("//", pos + 1);
}
const MntPair* mount = GetMount(corrected_path); const MntPair* mount = GetMount(normalized_path);
if (!mount) { if (!mount) {
return ""; return "";
} }
@ -47,81 +42,88 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view guest_directory, b
} }
// Nothing to do if getting the mount itself. // Nothing to do if getting the mount itself.
if (corrected_path == mount->mount) { if (normalized_path == mount->mount) {
return mount->host_path; return mount->host_path;
} }
// Remove device (e.g /app0) from path to retrieve relative path. // Remove device (e.g /app0) from path to retrieve relative path.
pos = mount->mount.size() + 1; const auto rel_path = std::string_view{normalized_path}.substr(mount->mount.size() + 1);
const auto rel_path = std::string_view(corrected_path).substr(pos);
std::filesystem::path host_path = mount->host_path / rel_path; std::filesystem::path host_path = mount->host_path / rel_path;
std::filesystem::path patch_path = mount->host_path; std::filesystem::path patch_path = mount->host_path;
patch_path += "-UPDATE"; patch_path += "-UPDATE";
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) && patch_path /= rel_path;
std::filesystem::exists(patch_path / rel_path)) {
host_path = patch_path / rel_path; if ((normalized_path.starts_with("/app0") || normalized_path.starts_with("/hostapp")) &&
std::filesystem::exists(patch_path)) {
return patch_path;
} }
if (!NeedsCaseInsensitiveSearch) { if (!NeedsCaseInsensitiveSearch) {
return host_path; return host_path;
} }
// If the path does not exist attempt to verify this. const auto search = [&](const auto host_path) {
// Retrieve parent path until we find one that exists. // If the path does not exist attempt to verify this.
std::scoped_lock lk{m_mutex}; // Retrieve parent path until we find one that exists.
path_parts.clear(); std::scoped_lock lk{m_mutex};
auto current_path = host_path; path_parts.clear();
while (!std::filesystem::exists(current_path)) { auto current_path = host_path;
// We have probably cached this if it's a folder. while (!std::filesystem::exists(current_path)) {
if (auto it = path_cache.find(current_path); it != path_cache.end()) { // We have probably cached this if it's a folder.
current_path = it->second; if (auto it = path_cache.find(current_path); it != path_cache.end()) {
break; current_path = it->second;
break;
}
path_parts.emplace_back(current_path.filename());
current_path = current_path.parent_path();
} }
path_parts.emplace_back(current_path.filename()); // We have found an anchor. Traverse parts we recoded and see if they
current_path = current_path.parent_path(); // exist in filesystem but in different case.
} auto guest_path = current_path;
while (!path_parts.empty()) {
// We have found an anchor. Traverse parts we recoded and see if they const auto part = path_parts.back();
// exist in filesystem but in different case. const auto add_match = [&](const auto& host_part) {
auto guest_path = current_path; current_path /= host_part;
while (!path_parts.empty()) { guest_path /= part;
const auto part = path_parts.back(); path_cache[guest_path] = current_path;
const auto add_match = [&](const auto& host_part) { path_parts.pop_back();
current_path /= host_part; };
guest_path /= part; // Can happen when the mismatch is in upper folder.
path_cache[guest_path] = current_path; if (std::filesystem::exists(current_path / part)) {
path_parts.pop_back(); add_match(part);
};
// Can happen when the mismatch is in upper folder.
if (std::filesystem::exists(current_path / part)) {
add_match(part);
continue;
}
const auto part_low = Common::ToLower(part.string());
bool found_match = false;
for (const auto& path : std::filesystem::directory_iterator(current_path)) {
const auto candidate = path.path().filename();
const auto filename = Common::ToLower(candidate.string());
// Check if a filename matches in case insensitive manner.
if (filename != part_low) {
continue; continue;
} }
// We found a match, record the actual path in the cache. const auto part_low = Common::ToLower(part.string());
add_match(candidate); bool found_match = false;
found_match = true; for (const auto& path : std::filesystem::directory_iterator(current_path)) {
break; const auto candidate = path.path().filename();
} const auto filename = Common::ToLower(candidate.string());
if (!found_match) { // Check if a filename matches in case insensitive manner.
// Opening the guest path will surely fail but at least gives if (filename != part_low) {
// a better error message than the empty path. continue;
return host_path; }
// We found a match, record the actual path in the cache.
add_match(candidate);
found_match = true;
break;
}
if (!found_match) {
return std::optional<std::filesystem::path>({});
}
} }
return std::optional<std::filesystem::path>(current_path);
};
if (const auto path = search(patch_path)) {
return *path;
}
if (const auto path = search(host_path)) {
return *path;
} }
// The path was found. // Opening the guest path will surely fail but at least gives
return current_path; // a better error message than the empty path.
return host_path;
} }
int HandleTable::CreateHandle() { int HandleTable::CreateHandle() {