mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
filesystem: keep unmapped guest paths out of the host namespace
This commit is contained in:
@@ -54,7 +54,7 @@ struct sys_file_t {
|
||||
#endif
|
||||
|
||||
static std::filesystem::path get_internal_name(const std::filesystem::path& name) {
|
||||
return name.is_absolute() ? name : (std::filesystem::path(".") / name);
|
||||
return name.empty() || name.is_absolute() ? name : (std::filesystem::path(".") / name);
|
||||
}
|
||||
|
||||
// Pass access-pattern hints to the host.
|
||||
|
||||
@@ -358,7 +358,8 @@ std::filesystem::path MountPoints::ResolvePath(const std::string& mounted_name)
|
||||
#endif
|
||||
}
|
||||
|
||||
return mounted_name;
|
||||
// Unmounted guest paths must not fall through to the host filesystem.
|
||||
return {};
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
@@ -461,6 +462,10 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) {
|
||||
}
|
||||
|
||||
file->real_name = g_mount_points->ResolvePath(file->name);
|
||||
if (file->real_name.empty()) {
|
||||
g_files->DeleteDescriptor(descriptor);
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
if (trunc && rw_mode == Common::File::Mode::Read) {
|
||||
g_files->DeleteDescriptor(descriptor);
|
||||
@@ -1255,7 +1260,7 @@ int KYTY_SYSV_ABI KernelRename(const char* from, const char* to) {
|
||||
auto real_from = g_mount_points->ResolvePath(from_path);
|
||||
auto real_to = g_mount_points->ResolvePath(to_path);
|
||||
|
||||
if (!Common::File::IsFileExisting(real_from)) {
|
||||
if (real_to.empty() || !Common::File::IsFileExisting(real_from)) {
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
@@ -1335,6 +1340,9 @@ int KYTY_SYSV_ABI KernelMkdir(const char* path, uint16_t mode) {
|
||||
path, mode);
|
||||
|
||||
auto real_name = g_mount_points->ResolvePath(std::string(path));
|
||||
if (real_name.empty()) {
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
if (Common::File::IsDirectoryExisting(real_name)) {
|
||||
return KERNEL_ERROR_EEXIST;
|
||||
|
||||
@@ -54,6 +54,7 @@ struct Lifecycle {
|
||||
|
||||
void Mount(const std::filesystem::path& folder, const std::string& point);
|
||||
void Umount(const std::string& folder_or_point);
|
||||
// Returns an empty path when the guest path has no mounted filesystem.
|
||||
std::filesystem::path GetRealFilename(const std::string& mounted_file_name);
|
||||
|
||||
int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode);
|
||||
|
||||
@@ -132,7 +132,7 @@ void CheckMountRoot(const std::filesystem::path &root) {
|
||||
FileSystem::Mount(root, "/app0");
|
||||
Check(FileSystem::GetRealFilename("/app0/rpf.cache") == root / "rpf.cache",
|
||||
"resolve mount descendant");
|
||||
Check(FileSystem::GetRealFilename("/app01/rpf.cache") == "/app01/rpf.cache",
|
||||
Check(FileSystem::GetRealFilename("/app01/rpf.cache").empty(),
|
||||
"mount prefix must end at a path component");
|
||||
|
||||
for (const char *path : {"/app0", "/app0/"}) {
|
||||
@@ -162,18 +162,58 @@ void CheckMountRoot(const std::filesystem::path &root) {
|
||||
}
|
||||
}
|
||||
FileSystem::Umount("/app0");
|
||||
Check(FileSystem::GetRealFilename("/app0/rpf.cache") == "/app0/rpf.cache",
|
||||
Check(FileSystem::GetRealFilename("/app0/rpf.cache").empty(),
|
||||
"unmount by guest path");
|
||||
for (const auto &folder : {root, root / ""}) {
|
||||
for (const auto &host : {root, root / ""}) {
|
||||
FileSystem::Mount(folder, "/app0");
|
||||
FileSystem::Umount(Common::PathToGenericString(host));
|
||||
Check(FileSystem::GetRealFilename("/app0/rpf.cache") == "/app0/rpf.cache",
|
||||
Check(FileSystem::GetRealFilename("/app0/rpf.cache").empty(),
|
||||
"unmount by host path with or without trailing separator");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckUnmappedPaths(const std::filesystem::path &root) {
|
||||
const auto host_file = root / "host-only.dat";
|
||||
const auto host_path = Common::PathToGenericString(host_file);
|
||||
Common::File fixture;
|
||||
Check(fixture.Create(host_file), "create unmapped host file");
|
||||
fixture.Close();
|
||||
|
||||
FileSystem::FileStat stat {};
|
||||
Check(FileSystem::GetRealFilename(host_path).empty() &&
|
||||
FileSystem::KernelOpen(host_path.c_str(), 0, 0) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
FileSystem::KernelStat(host_path.c_str(), &stat) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
FileSystem::KernelCheckReachability(host_path.c_str()) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT,
|
||||
"existing host files are absent from the guest namespace");
|
||||
Check(FileSystem::KernelUnlink(host_path.c_str()) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
FileSystem::KernelRmdir(Common::PathToGenericString(root).c_str()) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
std::filesystem::exists(host_file),
|
||||
"unmapped host files and directories cannot be removed");
|
||||
|
||||
const auto missing = Common::PathToGenericString(root / "unmapped-create");
|
||||
Check(FileSystem::KernelOpen(missing.c_str(), 0x601, 0777) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
FileSystem::KernelMkdir(missing.c_str(), 0777) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
!std::filesystem::exists(root / "unmapped-create"),
|
||||
"creation requires a mounted guest destination");
|
||||
|
||||
FileSystem::Mount(root, "/app0");
|
||||
Check(FileSystem::KernelRename("/app0/host-only.dat", missing.c_str()) ==
|
||||
Libs::LibKernel::KERNEL_ERROR_ENOENT &&
|
||||
std::filesystem::exists(host_file) &&
|
||||
!std::filesystem::exists(root / "unmapped-create"),
|
||||
"rename to an unmapped destination preserves the source");
|
||||
FileSystem::Umount("/app0");
|
||||
}
|
||||
|
||||
void CheckUnicodePaths(const std::filesystem::path &root) {
|
||||
constexpr std::string_view HostDirectory =
|
||||
"Test\xc3\xa9-\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
|
||||
@@ -548,6 +588,7 @@ int main() {
|
||||
TempDirectory temporary;
|
||||
FileSystem::Initialize();
|
||||
CheckMountRoot(temporary.Path());
|
||||
CheckUnmappedPaths(temporary.Path());
|
||||
CheckUnicodePaths(temporary.Path());
|
||||
CheckUnicodeLogPath(temporary.Path());
|
||||
CheckDirectoryStream(temporary.Path());
|
||||
|
||||
Reference in New Issue
Block a user