filesystem: keep unmapped guest paths out of the host namespace

This commit is contained in:
nmzik
2026-09-28 18:39:03 +04:00
parent 05f92b6422
commit a107d1e333
4 changed files with 56 additions and 6 deletions
+1 -1
View File
@@ -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.
+10 -2
View File
@@ -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;
+1
View File
@@ -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);
+44 -3
View File
@@ -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());