diff --git a/src/common/io_file.h b/src/common/io_file.h index cb01e154a..c6eb3b563 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -213,13 +213,13 @@ public: IOFile out(path, FileAccessMode::Write); return out.Write(data); } + std::FILE* file = nullptr; private: std::filesystem::path file_path; FileAccessMode file_access_mode{}; FileType file_type{}; - std::FILE* file = nullptr; uintptr_t file_mapping = 0; }; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 619a15ab6..ba1bd120f 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -5,6 +5,7 @@ #include #include "common/assert.h" +#include "common/error.h" #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" @@ -24,6 +25,13 @@ #include "core/memory.h" #include "kernel.h" +#ifdef _WIN32 +#include +#include +#else +#include +#endif + namespace D = Core::Devices; using FactoryDevice = std::function(u32, const char*, int, u16)>; @@ -1068,6 +1076,295 @@ s32 PS4_SYSV_ABI sceKernelUnlink(const char* path) { return result; } +#ifdef _WIN32 +#define __FD_SETSIZE 1024 + +typedef struct { + unsigned long fds_bits[__FD_SETSIZE / (8 * sizeof(unsigned long))]; +} fd_set_posix; + +#define FD_SET_POSIX(fd, set) \ + ((set)->fds_bits[(fd) / (8 * sizeof(unsigned long))] |= \ + (1UL << ((fd) % (8 * sizeof(unsigned long))))) + +#define FD_CLR_POSIX(fd, set) \ + ((set)->fds_bits[(fd) / (8 * sizeof(unsigned long))] &= \ + ~(1UL << ((fd) % (8 * sizeof(unsigned long))))) + +#define FD_ISSET_POSIX(fd, set) \ + (((set)->fds_bits[(fd) / (8 * sizeof(unsigned long))] & \ + (1UL << ((fd) % (8 * sizeof(unsigned long))))) != 0) + +#define FD_ZERO_POSIX(set) memset((set), 0, sizeof(fd_set_posix)) + +s32 PS4_SYSV_ABI posix_select(s32 nfds, fd_set_posix* readfds, fd_set_posix* writefds, + fd_set_posix* exceptfds, OrbisKernelTimeval* timeout) { + LOG_DEBUG(Kernel_Fs, "nfds = {}, readfds = {}, writefds = {}, exceptfds = {}, timeout = {}", + nfds, fmt::ptr(readfds), fmt::ptr(writefds), fmt::ptr(exceptfds), fmt::ptr(timeout)); + + auto* h = Common::Singleton::Instance(); + + fd_set read_host = {}, write_host = {}, except_host = {}; + FD_ZERO(&read_host); + FD_ZERO(&write_host); + FD_ZERO(&except_host); + + fd_set_posix read_ready, write_ready, except_ready; + FD_ZERO_POSIX(&read_ready); + FD_ZERO_POSIX(&write_ready); + FD_ZERO_POSIX(&except_ready); + + std::map host_to_guest; + s32 socket_max_fd = -1; + + for (s32 i = 0; i < nfds; ++i) { + bool want_read = readfds && FD_ISSET_POSIX(i, readfds); + bool want_write = writefds && FD_ISSET_POSIX(i, writefds); + bool want_except = exceptfds && FD_ISSET_POSIX(i, exceptfds); + if (!(want_read || want_write || want_except)) { + continue; + } + + auto* file = h->GetFile(i); + if (!file || ((file->type == Core::FileSys::FileType::Regular && !file->f.IsOpen()) || + (file->type == Core::FileSys::FileType::Socket && !file->is_opened))) { + LOG_ERROR(Kernel_Fs, "fd {} is null or not opened", i); + *__Error() = POSIX_EBADF; + return -1; + } + + s32 native_fd = -1; + switch (file->type) { + case Core::FileSys::FileType::Regular: + native_fd = static_cast(file->f.GetFileMapping()); + break; + case Core::FileSys::FileType::Socket: { + auto sock = file->socket->Native(); + native_fd = sock ? static_cast(*sock) : -1; + break; + } + case Core::FileSys::FileType::Device: + native_fd = -1; + break; + default: + UNREACHABLE(); + break; + } + + if (file->type == Core::FileSys::FileType::Regular || + file->type == Core::FileSys::FileType::Device) { + // Disk files always ready + if (want_read) { + FD_SET_POSIX(i, &read_ready); + } + if (want_write) { + FD_SET_POSIX(i, &write_ready); + } + // exceptfds not supported on regular files + } else if (file->type == Core::FileSys::FileType::Socket) { + if (want_read) { + FD_SET(native_fd, &read_host); + } + if (want_write) { + FD_SET(native_fd, &write_host); + } + if (want_except) { + FD_SET(native_fd, &except_host); + } + socket_max_fd = std::max(socket_max_fd, native_fd); + } + + if (native_fd == -1) { + continue; + } + + host_to_guest[native_fd] = i; + } + + LOG_DEBUG(Kernel_Fs, + "Before select(): read_host.fd_count = {}, write_host.fd_count = {}, " + "except_host.fd_count = {}", + read_host.fd_count, write_host.fd_count, except_host.fd_count); + + if (read_host.fd_count == 0 && write_host.fd_count == 0 && except_host.fd_count == 0) { + LOG_WARNING(Kernel_Fs, "No sockets in fd_sets, select() will return immediately"); + } + + if (readfds) { + FD_ZERO_POSIX(readfds); + } + if (writefds) { + FD_ZERO_POSIX(writefds); + } + if (exceptfds) { + FD_ZERO_POSIX(exceptfds); + } + + s32 result = 0; + if (socket_max_fd != -1) { + timeval tv = {}; + timeval* tv_ptr = nullptr; + if (timeout) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_usec; + tv_ptr = &tv; + } + result = select(0, read_host.fd_count > 0 ? &read_host : nullptr, + write_host.fd_count > 0 ? &write_host : nullptr, + except_host.fd_count > 0 ? &except_host : nullptr, tv_ptr); + if (result == SOCKET_ERROR) { + s32 err = WSAGetLastError(); + LOG_ERROR(Kernel_Fs, "select() failed with error {}", err); + switch (err) { + case WSAEFAULT: + *__Error() = POSIX_EFAULT; + break; + case WSAEINVAL: + *__Error() = POSIX_EINVAL; + break; + case WSAENOBUFS: + *__Error() = POSIX_ENOBUFS; + break; + default: + LOG_ERROR(Kernel_Fs, "Unhandled error case {}", err); + break; + } + return -1; + } + + for (s32 i = 0; i < read_host.fd_count; ++i) { + s32 fd = static_cast(read_host.fd_array[i]); + FD_SET_POSIX(host_to_guest[fd], readfds); + } + for (s32 i = 0; i < write_host.fd_count; ++i) { + s32 fd = static_cast(write_host.fd_array[i]); + FD_SET_POSIX(host_to_guest[fd], writefds); + } + for (s32 i = 0; i < except_host.fd_count; ++i) { + s32 fd = static_cast(except_host.fd_array[i]); + FD_SET_POSIX(host_to_guest[fd], exceptfds); + } + } + + // Add regular/device files ready count + s32 disk_ready = 0; + for (s32 i = 0; i < nfds; ++i) { + if (FD_ISSET_POSIX(i, &read_ready)) { + FD_SET_POSIX(i, readfds); + disk_ready++; + } + if (FD_ISSET_POSIX(i, &write_ready)) { + FD_SET_POSIX(i, writefds); + disk_ready++; + } + } + + return result + disk_ready; +} +#else +s32 PS4_SYSV_ABI posix_select(s32 nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, + OrbisKernelTimeval* timeout) { + LOG_DEBUG(Kernel_Fs, "nfds = {}, readfds = {}, writefds = {}, exceptfds = {}, timeout = {}", + nfds, fmt::ptr(readfds), fmt::ptr(writefds), fmt::ptr(exceptfds), fmt::ptr(timeout)); + + auto* h = Common::Singleton::Instance(); + fd_set read_host, write_host, except_host; + FD_ZERO(&read_host); + FD_ZERO(&write_host); + FD_ZERO(&except_host); + + std::map host_to_guest; + s32 max_fd = -1; + + for (s32 i = 0; i < nfds; ++i) { + auto read = readfds && FD_ISSET(i, readfds); + auto write = writefds && FD_ISSET(i, writefds); + auto except = exceptfds && FD_ISSET(i, exceptfds); + if (read || write || except) { + auto* file = h->GetFile(i); + if (file == nullptr || + ((file->type == Core::FileSys::FileType::Regular && !file->f.IsOpen()) || + (file->type == Core::FileSys::FileType::Socket && !file->is_opened))) { + LOG_ERROR(Kernel_Fs, "fd {} is null or not opened", i); + *__Error() = POSIX_EBADF; + return -1; + } + + s32 native_fd = [&] { + switch (file->type) { + case Core::FileSys::FileType::Regular: + return static_cast(file->f.GetFileMapping()); + case Core::FileSys::FileType::Device: + return -1; + case Core::FileSys::FileType::Socket: { + auto sock = file->socket->Native(); + // until P2P sockets contain a proper socket + return sock ? static_cast(*sock) : -1; + } + default: + UNREACHABLE(); + } + }(); + if (native_fd == -1) { + continue; + } + host_to_guest.emplace(native_fd, i); + + max_fd = std::max(max_fd, native_fd); + + if (read) { + FD_SET(native_fd, &read_host); + } + if (write) { + FD_SET(native_fd, &write_host); + } + if (except) { + FD_SET(native_fd, &except_host); + } + } + } + + if (max_fd == -1) { + LOG_WARNING(Kernel_Fs, "all requested file descriptors are unsupported"); + return 0; + } + + s32 ret = select(max_fd + 1, &read_host, &write_host, &except_host, (timeval*)timeout); + + if (ret > 0) { + if (readfds) { + FD_ZERO(readfds); + } + if (writefds) { + FD_ZERO(writefds); + } + if (exceptfds) { + FD_ZERO(exceptfds); + } + + for (s32 i = 0; i < max_fd + 1; ++i) { + if (readfds && FD_ISSET(i, &read_host)) { + FD_SET(host_to_guest[i], readfds); + } + if (writefds && FD_ISSET(i, &write_host)) { + FD_SET(host_to_guest[i], writefds); + } + if (exceptfds && FD_ISSET(i, &except_host)) { + FD_SET(host_to_guest[i], exceptfds); + } + } + } + if (ret < 0) { + s32 error = errno; + LOG_ERROR(Kernel_Fs, "native select call failed with {} ({})", error, + Common::NativeErrorToString(error)); + SetPosixErrno(error); + } + + return ret; +} +#endif + void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("6c3rCVE-fTU", "libkernel", 1, "libkernel", 1, 1, open); LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open); @@ -1125,6 +1422,8 @@ void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("nKWi-N2HBV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPwrite); LIB_FUNCTION("mBd4AfLP+u8", "libkernel", 1, "libkernel", 1, 1, sceKernelPwritev); LIB_FUNCTION("AUXVxWeJU-A", "libkernel", 1, "libkernel", 1, 1, sceKernelUnlink); + LIB_FUNCTION("T8fER+tIGgk", "libScePosix", 1, "libkernel", 1, 1, posix_select); + LIB_FUNCTION("T8fER+tIGgk", "libkernel", 1, "libkernel", 1, 1, posix_select); } } // namespace Libraries::Kernel diff --git a/src/core/libraries/network/sockets.h b/src/core/libraries/network/sockets.h index ff2cef556..42fe1bcd7 100644 --- a/src/core/libraries/network/sockets.h +++ b/src/core/libraries/network/sockets.h @@ -58,6 +58,7 @@ struct Socket { virtual int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) = 0; virtual int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) = 0; virtual int fstat(Libraries::Kernel::OrbisKernelStat* stat) = 0; + virtual std::optional Native() = 0; std::mutex m_mutex; std::mutex receive_mutex; }; @@ -93,6 +94,9 @@ struct PosixSocket : public Socket { int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; + std::optional Native() override { + return sock; + } }; struct P2PSocket : public Socket { @@ -113,6 +117,9 @@ struct P2PSocket : public Socket { int GetSocketAddress(OrbisNetSockaddr* name, u32* namelen) override; int GetPeerName(OrbisNetSockaddr* addr, u32* namelen) override; int fstat(Libraries::Kernel::OrbisKernelStat* stat) override; + std::optional Native() override { + return {}; + } }; } // namespace Libraries::Net \ No newline at end of file