Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/libs/prx/libSceLibcInternal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp UserContext.cpp)
target_include_directories(libSceLibcInternal PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceLibcInternal PRIVATE ${CMAKE_DL_LIBS})
target_sources(libSceLibcInternal PRIVATE Directory.cpp)
configure_windows_unwind(libSceLibcInternal)
target_sources(libSceLibcInternal PRIVATE FilesystemStatistics.cpp)

Expand All @@ -20,6 +21,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_fstatvfs PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
endif()
add_test_executable(guest_fdopendir_tests ${LIBS_INCLUDE_DIR}/tests/GuestFdopendir.cpp)
target_include_directories(guest_fdopendir_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_fdopendir_tests PRIVATE libSceLibcInternal libkernel libc)
configure_windows_unwind(guest_fdopendir_tests)
add_test(NAME guest_fdopendir COMMAND guest_fdopendir_tests)
if(WIN32)
set_tests_properties(guest_fdopendir PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
endif()
add_test_executable(guest_utime_forwarding_tests ${LIBS_INCLUDE_DIR}/tests/GuestUtimeForwarding.cpp FileTime.cpp)
target_include_directories(guest_utime_forwarding_tests PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(guest_utime_forwarding_tests)
Expand Down
16 changes: 16 additions & 0 deletions core/libs/prx/libSceLibcInternal/Directory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "prx/libc/include/GuestDirectory.hpp"
#include "prx/libkernel/File/include/DirectoryDescriptor.hpp"
#include <cerrno>

extern "C" int APS5_VABI close_nid_postfix(int d);
extern "C" int* APS5_VABI __error_nid_postfix();

extern "C" void* APS5_VABI fdopendir_nid_postfix(int fd) {
int error = 0;
const char* path = GetDirectoryDescriptorPath_nid_no_patch(fd, &error);
if (path == nullptr) {
*__error_nid_postfix() = error == EBADF ? 9 : 20;
return nullptr;
}
return OpenDescriptorDirectory_nid_no_patch(path, fd, close_nid_postfix);
}
3 changes: 3 additions & 0 deletions core/libs/prx/libc/include/GuestDirectory.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstddef>
#include <cstdint>

Expand All @@ -12,3 +13,5 @@ struct GuestDirectoryEntry {
};
static_assert(offsetof(GuestDirectoryEntry, name) == 8);
static_assert(sizeof(GuestDirectoryEntry) == 264);

extern "C" void* APS5_VABI OpenDescriptorDirectory_nid_no_patch(const char* hostPath, int descriptor, int (APS5_VABI* closeDescriptor)(int));
18 changes: 17 additions & 1 deletion core/libs/prx/libc/src/Directory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ struct Directory {
std::filesystem::path path;
GuestDirectoryEntry entry{};
std::mutex mutex;
int descriptor = -1;
int (APS5_VABI* closeDescriptor)(int) = nullptr;
~Directory() { if (native) CloseNative(native); }
};
std::uint8_t DirectoryType(const std::filesystem::path& path) {
Expand Down Expand Up @@ -120,9 +122,10 @@ GuestDirectoryEntry* APS5_VABI readdir_nid_postfix(void* handle) {
int APS5_VABI closedir_nid_postfix(void* handle) {
if (!handle) { errno = 9; return -1; }
std::unique_ptr<Directory> directory(static_cast<Directory*>(handle));
const int result = CloseNative(directory->native);
int result = CloseNative(directory->native);
directory->native = nullptr;
if (result) errno = DirectoryError(errno);
if (directory->closeDescriptor && directory->closeDescriptor(directory->descriptor) != 0) result = -1;
return result;
}

Expand All @@ -132,4 +135,17 @@ void APS5_VABI rewinddir_nid_postfix(void* handle) {
std::lock_guard lock(directory.mutex);
RewindNative(directory.native);
}

void* APS5_VABI OpenDescriptorDirectory_nid_no_patch(const char* hostPath, int descriptor, int (APS5_VABI* closeDescriptor)(int)) {
try {
auto directory = std::make_unique<Directory>();
directory->path = std::filesystem::path(reinterpret_cast<const char8_t*>(hostPath));
directory->native = OpenNative(directory->path);
if (!directory->native) { errno = DirectoryError(errno); return nullptr; }
directory->descriptor = descriptor;
directory->closeDescriptor = closeDescriptor;
return directory.release();
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
catch (const std::filesystem::filesystem_error&) { errno = 5; return nullptr; }
}
}
49 changes: 49 additions & 0 deletions core/libs/tests/GuestFdopendir.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "prx/libc/include/GuestDirectory.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/File/include/File.hpp"
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <map>
#include <string>
extern "C" {
void* APS5_VABI fdopendir_nid_postfix(int);
GuestDirectoryEntry* APS5_VABI readdir_nid_postfix(void*);
int APS5_VABI closedir_nid_postfix(void*);
int APS5_VABI open_nid_postfix(const char*, int, int);
int APS5_VABI close_nid_postfix(int);
int* APS5_VABI __error_nid_postfix();
}
static void Require(bool value) { if (!value) std::abort(); }
int main() {
const auto root = std::filesystem::path("anyps5-fdopendir-test-" +
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
Require(std::filesystem::create_directory(root));
Require(std::filesystem::create_directory(root / "subdirectory"));
{ std::ofstream file(root / "sample.txt"); file << "test"; }
const int fd = open_nid_postfix(root.string().c_str(), 0x20000, 0);
Require(fd >= 0);
void* directory = fdopendir_nid_postfix(fd);
Require(directory != nullptr);
std::map<std::string, int> entries;
while (auto* entry = readdir_nid_postfix(directory)) {
entries[entry->name] = entry->type;
}
Require(entries.at("sample.txt") == 8);
Require(entries.at("subdirectory") == 4);
Require(DescriptorIsOpen_nid_no_patch(fd));
Require(closedir_nid_postfix(directory) == 0);
Require(!DescriptorIsOpen_nid_no_patch(fd));
Require(fdopendir_nid_postfix(fd) == nullptr && *__error_nid_postfix() == 9);
Require(fdopendir_nid_postfix(-1) == nullptr && *__error_nid_postfix() == 9);
const int file = open_nid_postfix((root / "sample.txt").string().c_str(), 0, 0);
Require(file >= 0);
Require(fdopendir_nid_postfix(file) == nullptr && *__error_nid_postfix() == 20);
Require(DescriptorIsOpen_nid_no_patch(file));
Require(close_nid_postfix(file) == 0);
std::filesystem::remove(root / "sample.txt");
std::filesystem::remove(root / "subdirectory");
std::filesystem::remove(root);
}
1 change: 1 addition & 0 deletions docs/dev/debt/libSceLibcInternal.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
- [makecontext](../../../core/libs/prx/libSceLibcInternal/UserContext.cpp) (libSceLibcInternal) follows FreeBSD 11's `lib/libc/amd64/gen/makecontext.c`: at most six arguments, a stack of at least `MINSIGSTKSZ` (2048) bytes, and `exit(0)` when the function returns with no `uc_link`. The console's version is not verified
- [makecontext](../../../core/libs/prx/libSceLibcInternal/UserContext.cpp) (libSceLibcInternal) on Windows pins its stack writable until a later `makecontext` stack overlaps it, since nothing tells the library when the guest frees the stack
- [Thread exit destructors](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) run when a libkernel thread finishes; on the main or a host thread only `_sceLibcInternalThreadDtors` runs them, as `exit` does, else they are dropped
- [fdopendir](../../../core/libs/prx/libSceLibcInternal/Directory.cpp) (libSceLibcInternal) - the directory stream starts at the directory's first entry, while FreeBSD's [`fdopendir`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/lib/libc/gen/opendir.c#L67-L74) reads from the descriptor's current offset, and `FD_CLOEXEC` is not set on the descriptor. As in FreeBSD, a descriptor that is not open fails with `EBADF`, one that is not a directory with `ENOTDIR` (both leave it open), and `closedir` closes it
- [fstatvfs](../../../core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp) (libSceLibcInternal) converts `_fstatfs` to the 88-byte `struct statvfs` as FreeBSD 9.2's `sfs2svfs` does (`f_bsize` from the transfer size, `f_frsize` from the block size, `f_favail` equal to `f_ffree`, `f_fsid` 0, `ST_RDONLY` and `ST_NOSUID` only). Differences: `f_namemax` is the `statfs` value, not `fpathconf(_PC_NAME_MAX)`; negative `f_bavail` and `f_ffree` wrap to unsigned without `EOVERFLOW`, as the same-width copy in FreeBSD does; inode counts are 0 on Windows, inherited from `_fstatfs`; a null output returns `EFAULT` after the descriptor is validated, where FreeBSD would fault. Layout and conversion are unverified against the PS5 library (PS5MC imports `fstatvfs` but was not run)
Loading