Merge pull request #4569 from pipe1os/feat/libkernel-fdopendir
feat(libSceLibcInternal): implement fdopendir
This commit is contained in:
commit
28c9dae73d
|
|
@ -1,6 +1,7 @@
|
|||
add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp Getopt.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)
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -10,5 +10,6 @@
|
|||
- [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)
|
||||
- [getopt, getopt_long](../../../core/libs/prx/libSceLibcInternal/Getopt.cpp) (libSceLibcInternal) follow FreeBSD 11 [getopt.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/getopt.c) and [getopt_long.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/getopt_long.c) with `GNU_COMPATIBLE`: `getopt` does not permute, `getopt_long` permutes unless `POSIXLY_CORRECT` is set in the guest environment or the option string starts with `+`, and the exported `opterr`, `optind`, `optopt`, `optreset` and `optarg` start at 1, 1, 0, 0 and null. Warnings go to the guest's current `stderr` prefixed with `__progname`, where FreeBSD's `warnx` keeps the `stderr` of its first call. A null `getopt` option string, a null argument vector or argument, a negative `optind` and an `optind` moved inside a pending permutation throw where FreeBSD faults. `getopt_long_only` is not exported. That the console's `libSceLibcInternal` uses this FreeBSD code is unverified
|
||||
|
|
|
|||
Loading…
Reference in New Issue