diff --git a/src/kernel/fileSystem.cpp b/src/kernel/fileSystem.cpp index ba2ae845d..406c8878a 100644 --- a/src/kernel/fileSystem.cpp +++ b/src/kernel/fileSystem.cpp @@ -410,6 +410,11 @@ std::filesystem::path GetRealFilename(const std::string& mounted_file_name) { return g_mount_points->ResolvePath(mounted_file_name); } +bool IsOpenDescriptor(int d) { + const auto* file = g_files->GetFile(d); + return file != nullptr && file->opened; +} + // NOLINTNEXTLINE(readability-function-cognitive-complexity) int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) { PRINT_NAME(); diff --git a/src/kernel/fileSystem.h b/src/kernel/fileSystem.h index 6a76008ad..02c8b0a5a 100644 --- a/src/kernel/fileSystem.h +++ b/src/kernel/fileSystem.h @@ -56,6 +56,8 @@ void Mount(const std::filesystem::path& folder, const std::stri 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); +// Returns whether d is an open file, directory or device descriptor. +bool IsOpenDescriptor(int d); int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode); int KYTY_SYSV_ABI KernelClose(int d); diff --git a/src/libs/network.cpp b/src/libs/network.cpp index efcd54160..c9689aca1 100644 --- a/src/libs/network.cpp +++ b/src/libs/network.cpp @@ -30,6 +30,7 @@ #include "common/emulatorConfig.h" #include "common/logging/log.h" #include "common/threads.h" +#include "kernel/fileSystem.h" #include "kernel/pthread.h" #include "libs/errno.h" #include "libs/libs.h" @@ -2577,6 +2578,7 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd std::vector> descriptors; std::vector> stream_reads; + std::vector> ready_files; int host_nfds = 0; for (int fd = 0; fd < nfds; fd++) { const bool read = GuestFdIsSet(readfds, fd); @@ -2585,6 +2587,16 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd if (!read && !write && !except) { continue; } + // FreeBSD polls files and devices ready to read and write at once. + if (!IsSocket(fd) && LibKernel::FileSystem::IsOpenDescriptor(fd)) { + if (read) { + ready_files.emplace_back(readfds, fd); + } + if (write) { + ready_files.emplace_back(writefds, fd); + } + continue; + } NativeSocket socket = INVALID_NATIVE_SOCKET; std::shared_ptr transport; #if !defined(_WIN32) @@ -2636,6 +2648,10 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd host_timeout.tv_usec = static_cast(guest_timeout->tv_usec); host_timeout_ptr = &host_timeout; } + if (!ready_files.empty()) { + host_timeout = {}; + host_timeout_ptr = &host_timeout; + } int result = 0; #if defined(_WIN32) @@ -2672,6 +2688,10 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd result++; } } + for (const auto& [fds, fd]: ready_files) { + GuestFdSet(fds, fd); + result++; + } return result; } diff --git a/tests/KernelFileSystemTests.cpp b/tests/KernelFileSystemTests.cpp index d9b4e813c..d65a28d7d 100644 --- a/tests/KernelFileSystemTests.cpp +++ b/tests/KernelFileSystemTests.cpp @@ -324,6 +324,7 @@ void CheckSaveRename(const std::filesystem::path &root, } void TestRandomDevices() { + namespace Net = Libs::Network::Net; for (const auto* path : {"/dev/urandom", "/dev/random"}) { const int fd = FileSystem::KernelOpen(path, 0, 0); FileSystem::FileStat stat {}; @@ -333,7 +334,18 @@ void TestRandomDevices() { std::array entropy {}; Check(FileSystem::KernelRead(fd, entropy.data(), entropy.size()) == entropy.size(), "character device supplies requested entropy"); + // OpenSSL RAND_poll selects the device before reading it; a null timeout must not block. + std::array readable {}; + readable[fd / 64] = uint64_t {1} << (fd % 64); + auto writable = readable; + const auto requested = readable; + Check(Net::Select(fd + 1, readable.data(), writable.data(), nullptr, nullptr) == 2 && + readable == requested && writable == requested, + "select reports an entropy source ready at once"); Check(FileSystem::KernelClose(fd) == OK, "close entropy source"); + Check(Net::Select(fd + 1, readable.data(), nullptr, nullptr, nullptr) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EBADF, + "select rejects a closed descriptor"); } } @@ -2453,8 +2465,8 @@ int main(int argc, char** argv) { TestAioBatches(); CheckSaveRename(temporary.Path(), "first-save"); CheckSaveRename(temporary.Path(), "replacement-save"); - FileSystem::Shutdown(); CheckSocketWakeup(); + FileSystem::Shutdown(); CheckEtherAddressFormatting(); #if defined(__linux__) TestNetCtlHostIpv4();