Fix mount root opens and empty APR prefixes (#439)

* Fix opening mount root directories

* Allow empty APR path prefixes

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
frangametv
2026-09-09 10:31:23 +02:00
committed by GitHub
parent 679adbfa5e
commit 1a3a15e4cf
3 changed files with 179 additions and 230 deletions
+18 -53
View File
@@ -50,8 +50,7 @@ public:
void Mount(const std::filesystem::path& folder, const std::string& point);
void Umount(const std::string& folder_or_point);
[[nodiscard]] std::filesystem::path GetRealFilename(const std::string& mounted_file_name);
[[nodiscard]] std::filesystem::path GetRealDirectory(const std::string& mounted_directory);
[[nodiscard]] std::filesystem::path ResolvePath(const std::string& mounted_name);
private:
std::vector<MountPair> m_mount_pairs;
@@ -284,26 +283,24 @@ static bool HasWindowsForbiddenFilenameCharacter(const std::string& relative_pat
}
#endif
std::filesystem::path MountPoints::GetRealFilename(const std::string& mounted_file_name) {
std::filesystem::path MountPoints::ResolvePath(const std::string& mounted_name) {
Common::LockGuard lock(m_mutex);
auto mounted_path =
Common::DirectoryWithoutFilename(Common::FixFilenameSlash(mounted_file_name));
// Match the entire guest path so a mount root works with or without a trailing slash.
const auto mounted_path = Common::FixDirectorySlash(mounted_name);
const auto it = std::find_if(
m_mount_pairs.begin(), m_mount_pairs.end(),
[&mounted_path](const MountPair& p) { return Common::StartsWith(mounted_path, p.point); });
if (it != m_mount_pairs.end()) {
const auto& p = *it;
auto rel_path =
Common::RemoveFirst(Common::FixFilenameSlash(mounted_file_name), p.point.size());
auto rel_path = Common::RemoveFirst(Common::FixFilenameSlash(mounted_name), p.point.size());
while (Common::StartsWith(rel_path, '/')) {
rel_path = Common::RemoveFirst(rel_path, 1);
}
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
if (HasWindowsForbiddenFilenameCharacter(rel_path)) {
::printf("FileSystem: Windows-incompatible guest filename: %s\n",
mounted_file_name.c_str());
mounted_name.c_str());
}
return p.dir / rel_path;
#else
@@ -311,32 +308,7 @@ std::filesystem::path MountPoints::GetRealFilename(const std::string& mounted_fi
#endif
}
return mounted_file_name;
}
std::filesystem::path MountPoints::GetRealDirectory(const std::string& mounted_directory) {
Common::LockGuard lock(m_mutex);
auto mounted_path = Common::FixDirectorySlash(mounted_directory);
const auto it = std::find_if(
m_mount_pairs.begin(), m_mount_pairs.end(),
[&mounted_path](const MountPair& p) { return Common::StartsWith(mounted_path, p.point); });
if (it != m_mount_pairs.end()) {
const auto& p = *it;
auto rel_path =
Common::RemoveFirst(Common::FixDirectorySlash(mounted_directory), p.point.size());
while (Common::StartsWith(rel_path, '/')) {
rel_path = Common::RemoveFirst(rel_path, 1);
}
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
return p.dir / rel_path;
#else
return ResolvePathIgnoringCase(p.dir / rel_path);
#endif
}
return mounted_directory;
return mounted_name;
}
void Initialize() {
@@ -370,7 +342,7 @@ void Umount(const std::string& folder_or_point) {
std::filesystem::path GetRealFilename(const std::string& mounted_file_name) {
return g_mount_points->GetRealFilename(mounted_file_name);
return g_mount_points->ResolvePath(mounted_file_name);
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
@@ -437,8 +409,7 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) {
return descriptor;
}
file->real_name = (directory ? g_mount_points->GetRealDirectory(file->name)
: g_mount_points->GetRealFilename(file->name));
file->real_name = g_mount_points->ResolvePath(file->name);
if (trunc && rw_mode == Common::File::Mode::Read) {
return KERNEL_ERROR_EACCES;
@@ -852,12 +823,9 @@ int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb) {
LOGF("\t KernelStat: %s\n", path);
std::string path_s = std::string(path);
auto real_file_name = g_mount_points->GetRealFilename(path_s);
auto real_directory = g_mount_points->GetRealDirectory(path_s);
auto real_file_name = g_mount_points->ResolvePath(path);
bool is_dir = Common::File::IsDirectoryExisting(real_file_name) ||
Common::File::IsDirectoryExisting(real_directory);
bool is_dir = Common::File::IsDirectoryExisting(real_file_name);
bool is_file = Common::File::IsFileExisting(real_file_name);
if (!is_dir && !is_file) {
@@ -1014,12 +982,9 @@ int KYTY_SYSV_ABI KernelUnlink(const char* path) {
return KERNEL_ERROR_EINVAL;
}
auto path_s = std::string(path);
auto real_file_name = g_mount_points->GetRealFilename(path_s);
auto real_directory = g_mount_points->GetRealDirectory(path_s);
auto real_file_name = g_mount_points->ResolvePath(path);
bool is_dir = Common::File::IsDirectoryExisting(real_file_name) ||
Common::File::IsDirectoryExisting(real_directory);
bool is_dir = Common::File::IsDirectoryExisting(real_file_name);
bool is_file = Common::File::IsFileExisting(real_file_name);
if (is_dir) {
@@ -1053,8 +1018,8 @@ int KYTY_SYSV_ABI KernelRename(const char* from, const char* to) {
auto from_path = std::string(from);
auto to_path = std::string(to);
auto real_from = g_mount_points->GetRealFilename(from_path);
auto real_to = g_mount_points->GetRealFilename(to_path);
auto real_from = g_mount_points->ResolvePath(from_path);
auto real_to = g_mount_points->ResolvePath(to_path);
if (!Common::File::IsFileExisting(real_from)) {
return KERNEL_ERROR_ENOENT;
@@ -1220,7 +1185,7 @@ int KYTY_SYSV_ABI KernelMkdir(const char* path, uint16_t mode) {
"\t mode = %04" PRIx16 "\n",
path, mode);
auto real_name = g_mount_points->GetRealDirectory(std::string(path));
auto real_name = g_mount_points->ResolvePath(std::string(path));
if (Common::File::IsDirectoryExisting(real_name)) {
return KERNEL_ERROR_EEXIST;
@@ -1246,7 +1211,7 @@ int KYTY_SYSV_ABI KernelRmdir(const char* path) {
LOGF("\t path = %s\n", path);
auto real_name = g_mount_points->GetRealDirectory(std::string(path));
auto real_name = g_mount_points->ResolvePath(std::string(path));
if (!Common::File::IsDirectoryExisting(real_name)) {
return KERNEL_ERROR_ENOENT;
@@ -1278,7 +1243,7 @@ int KYTY_SYSV_ABI KernelCheckReachability(const char* path) {
return OK;
}
auto real_name = g_mount_points->GetRealFilename(mounted_path);
auto real_name = g_mount_points->ResolvePath(mounted_path);
if (Common::File::IsFileExisting(real_name) || Common::File::IsDirectoryExisting(real_name)) {
return OK;
+46 -177
View File
@@ -101,109 +101,23 @@ static uint32_t ComputeFileId(const char* guest_path) {
return hash & static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
}
static bool CopyStringToOutput(const std::string& str, char* out, size_t out_size) {
if (str.empty() || str.size() + 1 > out_size) {
return false;
static int ReadGuestCString(uint64_t addr, char* out, size_t out_size) {
if (addr == 0) {
return LibKernel::KERNEL_ERROR_EFAULT;
}
std::memcpy(out, str.c_str(), str.size() + 1);
return true;
}
static bool ReadGuestCString(uint64_t addr, char* out, size_t out_size) {
if (out == nullptr || out_size == 0 || addr == 0) {
return false;
}
const auto max_len = static_cast<uint64_t>(out_size - 1);
const auto* src = reinterpret_cast<const char*>(static_cast<uintptr_t>(addr));
for (uint64_t pos = 0; pos < max_len; pos++) {
const auto* src = reinterpret_cast<const char*>(addr);
for (size_t pos = 0; pos < out_size; pos++) {
if (addr > std::numeric_limits<uint64_t>::max() - pos) {
out[pos] = '\0';
return false;
return LibKernel::KERNEL_ERROR_EFAULT;
}
const auto ch = src[pos];
out[pos] = ch;
if (ch == '\0') {
return pos != 0;
out[pos] = src[pos];
if (out[pos] == '\0') {
return OK;
}
}
out[max_len] = '\0';
return false;
}
static bool ReadGuestWideCString(uint64_t addr, char* out, size_t out_size) {
if (out == nullptr || out_size == 0 || addr == 0) {
return false;
}
std::array<char16_t, 1024> tmp {};
const auto max_len = std::min<uint64_t>(tmp.size() - 1, out_size - 1);
const auto* src = reinterpret_cast<const char*>(static_cast<uintptr_t>(addr));
for (uint64_t pos = 0; pos < max_len; pos++) {
if (pos > (std::numeric_limits<uint64_t>::max() - addr) / sizeof(char16_t)) {
return false;
}
char16_t ch = 0;
std::memcpy(&ch, src + pos * sizeof(char16_t), sizeof(ch));
tmp[static_cast<size_t>(pos)] = ch;
if (ch == u'\0') {
return pos != 0 && CopyStringToOutput(Common::Utf16ToUtf8(tmp.data()), out, out_size);
}
}
return false;
}
static bool ReadGuestPathText(uint64_t addr, char* out, size_t out_size) {
std::array<char, 1024> narrow {};
if (ReadGuestCString(addr, narrow.data(), std::min(narrow.size(), out_size))) {
const auto narrow_len = std::strlen(narrow.data());
if (narrow_len > 1 || !ReadGuestWideCString(addr, out, out_size)) {
std::memcpy(out, narrow.data(), std::strlen(narrow.data()) + 1);
}
return true;
}
if (ReadGuestWideCString(addr, out, out_size)) {
return true;
}
return false;
}
static bool ReadPathPointer(uint64_t pointer_addr, char* out, size_t out_size) {
uint64_t path_addr = 0;
if (!ReadGuest(pointer_addr, &path_addr)) {
return false;
}
if (path_addr == 0) {
return false;
}
if (ReadGuestPathText(path_addr, out, out_size)) {
return true;
}
return false;
}
static bool ResolvePathFromList(uint64_t path_list, uint64_t index, char* out, size_t out_size) {
if (ReadPathPointer(path_list + index * sizeof(uint64_t), out, out_size)) {
return true;
}
if (index != 0) {
return false;
}
if (ReadGuestPathText(path_list, out, out_size)) {
return true;
}
for (uint64_t offset = 0; offset < 0x40; offset += sizeof(uint64_t)) {
if (ReadPathPointer(path_list + offset, out, out_size)) {
return true;
}
}
return false;
return LibKernel::KERNEL_ERROR_ENAMETOOLONG;
}
static void RegisterHostPathLocked(uint32_t file_id, const std::string& host_path,
@@ -280,36 +194,6 @@ static int GetHostPathStat(const std::string& host_path, LibKernel::FileSystem::
return OK;
}
static bool JoinPrefixPath(const char* prefix, const char* path, char* out, size_t out_size) {
if (path == nullptr || out == nullptr || out_size == 0) {
return false;
}
if (prefix == nullptr || prefix[0] == '\0') {
std::strncpy(out, path, out_size - 1);
out[out_size - 1] = '\0';
return true;
}
const auto prefix_len = std::strlen(prefix);
const auto path_len = std::strlen(path);
const bool needs_sep = prefix_len != 0 && path_len != 0 && prefix[prefix_len - 1] != '/' &&
prefix[prefix_len - 1] != '\\' && path[0] != '/' && path[0] != '\\';
const auto total_len = prefix_len + (needs_sep ? 1u : 0u) + path_len;
if (total_len + 1u > out_size) {
return false;
}
std::memcpy(out, prefix, prefix_len);
auto pos = prefix_len;
if (needs_sep) {
out[pos++] = '/';
}
std::memcpy(out + pos, path, path_len);
out[total_len] = '\0';
return true;
}
static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size) {
if (guest_path == nullptr || guest_path[0] == '\0') {
return LibKernel::KERNEL_ERROR_EINVAL;
@@ -381,8 +265,8 @@ static int ResolveOnePath(const char* guest_path, uint32_t* id, uint64_t* size)
return OK;
}
static int ResolvePathsCommon(const void* path_list, uint64_t count, uint32_t* ids, uint64_t* sizes,
uint32_t* error_index, const char* prefix, int* results) {
static int ResolvePathsCommon(const char* const* path_list, uint32_t count, uint32_t* ids,
uint64_t* sizes, uint32_t* error_index, const char* prefix, int* results) {
if (path_list == nullptr || count == 0 || count > 1024 ||
(ids == nullptr && sizes == nullptr && results == nullptr)) {
return LibKernel::KERNEL_ERROR_EINVAL;
@@ -406,18 +290,27 @@ static int ResolvePathsCommon(const void* path_list, uint64_t count, uint32_t* i
return LibKernel::KERNEL_ERROR_EFAULT;
}
const auto path_list_addr = reinterpret_cast<uint64_t>(path_list);
bool any_error = false;
int first_error = OK;
uint32_t success_count = 0;
for (uint64_t i = 0; i < count; i++) {
char guest_path[1024] {};
char resolved_path[2048] {};
int result = OK;
if (!AprShared::ResolvePathFromList(path_list_addr, i, guest_path, sizeof(guest_path)) ||
!JoinPrefixPath(prefix, guest_path, resolved_path, sizeof(resolved_path))) {
result = LibKernel::KERNEL_ERROR_EFAULT;
} else {
if (!IsValidGuestRange(reinterpret_cast<uint64_t>(path_list), count * sizeof(*path_list))) {
return LibKernel::KERNEL_ERROR_EFAULT;
}
char prefix_buf[1024] {};
if (prefix != nullptr) {
const int result = ReadGuestCString(reinterpret_cast<uint64_t>(prefix), prefix_buf,
sizeof(prefix_buf));
if (result != OK) {
return result;
}
}
const auto prefix_size = std::strlen(prefix_buf);
uint32_t success_count = 0;
for (uint32_t i = 0; i < count; i++) {
char resolved_path[1024] {};
// Concatenate the prefix literally, including an empty prefix.
std::memcpy(resolved_path, prefix_buf, prefix_size);
int result = ReadGuestCString(reinterpret_cast<uint64_t>(path_list[i]),
resolved_path + prefix_size, sizeof(resolved_path) - prefix_size);
if (result == OK) {
result = ResolveOnePath(resolved_path, ids != nullptr ? &ids[i] : nullptr,
sizes != nullptr ? &sizes[i] : nullptr);
}
@@ -437,17 +330,13 @@ static int ResolvePathsCommon(const void* path_list, uint64_t count, uint32_t* i
if (error_index != nullptr) {
*error_index = static_cast<uint32_t>(i);
}
if (!any_error) {
any_error = true;
first_error = result;
}
if (results == nullptr) {
return result;
}
}
}
return results != nullptr ? static_cast<int>(success_count) : (any_error ? first_error : OK);
return results != nullptr ? static_cast<int>(success_count) : OK;
}
static uint32_t AllocateSubmissionId(uint64_t command_buffer, uint64_t result) {
@@ -521,7 +410,7 @@ static int KernelSyscallResult(int result) {
return -1;
}
static int KYTY_SYSV_ABI ResolveFilepathsToIds(const void* path_list, uint32_t count, uint32_t* ids,
static int KYTY_SYSV_ABI ResolveFilepathsToIds(const char* const* path_list, uint32_t count, uint32_t* ids,
uint32_t* error_index) {
PRINT_NAME();
@@ -582,7 +471,7 @@ static int KYTY_SYSV_ABI GetFileSize(uint32_t file_id, uint64_t* size) {
return OK;
}
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizes(const void* path_list, uint32_t count,
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizes(const char* const* path_list, uint32_t count,
uint32_t* ids, uint64_t* sizes,
uint32_t* error_index) {
PRINT_NAME();
@@ -591,37 +480,27 @@ static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizes(const void* path_list
AprShared::ResolvePathsCommon(path_list, count, ids, sizes, error_index, nullptr, nullptr));
}
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIds(const char* prefix, const void* path_list,
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIds(const char* prefix, const char* const* path_list,
uint32_t count, uint32_t* ids,
uint32_t* error_index) {
PRINT_NAME();
char prefix_buf[1024] {};
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
prefix_buf, sizeof(prefix_buf))) {
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
}
return KernelSyscallResult(AprShared::ResolvePathsCommon(path_list, count, ids, nullptr,
error_index, prefix_buf, nullptr));
error_index, prefix, nullptr));
}
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIdsAndFileSizes(const char* prefix,
const void* path_list,
const char* const* path_list,
uint32_t count, uint32_t* ids,
uint64_t* sizes,
uint32_t* error_index) {
PRINT_NAME();
char prefix_buf[1024] {};
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
prefix_buf, sizeof(prefix_buf))) {
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
}
return KernelSyscallResult(AprShared::ResolvePathsCommon(path_list, count, ids, sizes,
error_index, prefix_buf, nullptr));
error_index, prefix, nullptr));
}
static int KYTY_SYSV_ABI ResolveFilepathsToIdsForEach(const void* path_list, uint32_t count,
static int KYTY_SYSV_ABI ResolveFilepathsToIdsForEach(const char* const* path_list, uint32_t count,
uint32_t* ids, int* results) {
PRINT_NAME();
@@ -629,7 +508,7 @@ static int KYTY_SYSV_ABI ResolveFilepathsToIdsForEach(const void* path_list, uin
AprShared::ResolvePathsCommon(path_list, count, ids, nullptr, nullptr, nullptr, results));
}
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizesForEach(const void* path_list,
static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizesForEach(const char* const* path_list,
uint32_t count, uint32_t* ids,
uint64_t* sizes, int* results) {
PRINT_NAME();
@@ -639,32 +518,22 @@ static int KYTY_SYSV_ABI ResolveFilepathsToIdsAndFileSizesForEach(const void* pa
}
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIdsForEach(const char* prefix,
const void* path_list,
const char* const* path_list,
uint32_t count, uint32_t* ids,
int* results) {
PRINT_NAME();
char prefix_buf[1024] {};
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
prefix_buf, sizeof(prefix_buf))) {
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
}
return KernelSyscallResult(AprShared::ResolvePathsCommon(path_list, count, ids, nullptr,
nullptr, prefix_buf, results));
nullptr, prefix, results));
}
static int KYTY_SYSV_ABI ResolveFilepathsWithPrefixToIdsAndFileSizesForEach(
const char* prefix, const void* path_list, uint32_t count, uint32_t* ids, uint64_t* sizes,
const char* prefix, const char* const* path_list, uint32_t count, uint32_t* ids, uint64_t* sizes,
int* results) {
PRINT_NAME();
char prefix_buf[1024] {};
if (prefix != nullptr && !AprShared::ReadGuestPathText(reinterpret_cast<uint64_t>(prefix),
prefix_buf, sizeof(prefix_buf))) {
return KernelSyscallResult(LibKernel::KERNEL_ERROR_EFAULT);
}
return KernelSyscallResult(
AprShared::ResolvePathsCommon(path_list, count, ids, sizes, nullptr, prefix_buf, results));
AprShared::ResolvePathsCommon(path_list, count, ids, sizes, nullptr, prefix, results));
}
static int KYTY_SYSV_ABI SubmitCommandBufferAndGetResult(void* command_buffer, uint64_t,