From b15f0b5d366818a3415de3ca61ada86b88e42276 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 31 Oct 2023 18:21:38 -0700 Subject: [PATCH] FEXCore/FileLoading: Updates helper to load file that is backed by memory When attempting to read files that aren't backed by a filesystem then our current read file helpers fail since they query the file size upfront. Change the helper so that it doesn't query the size and just reads the file if it can be opened. This lets us read `/proc/self/maps` using helpers. --- FEXCore/Source/Utils/FileLoading.cpp | 38 ++++++++++++++++++---- FEXCore/unittests/APITests/FileLoading.cpp | 22 +++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 FEXCore/unittests/APITests/FileLoading.cpp diff --git a/FEXCore/Source/Utils/FileLoading.cpp b/FEXCore/Source/Utils/FileLoading.cpp index 14aec693b..71e6c8317 100644 --- a/FEXCore/Source/Utils/FileLoading.cpp +++ b/FEXCore/Source/Utils/FileLoading.cpp @@ -24,24 +24,48 @@ static bool LoadFileImpl(T &Data, const fextl::string &Filepath, size_t FixedSiz size_t FileSize{}; if (FixedSize == 0) { struct stat buf; - if (fstat(FD, &buf) != 0) { - close(FD); - return false; + if (fstat(FD, &buf) == 0) { + FileSize = buf.st_size; } - - FileSize = buf.st_size; } else { FileSize = FixedSize; } ssize_t Read = -1; - if (FileSize > 0) { + bool LoadedFile{}; + if (FileSize) { + // File size is known upfront Data.resize(FileSize); Read = pread(FD, &Data.at(0), FileSize, 0); + + LoadedFile = Read == FileSize; + } + else { + // The file is either empty or its size is unknown (e.g. procfs data). + // Try reading in chunks instead + ssize_t CurrentOffset = 0; + constexpr size_t READ_SIZE = 4096; + Data.resize(READ_SIZE); + + while ((Read = pread(FD, &Data.at(CurrentOffset), READ_SIZE, CurrentOffset)) == READ_SIZE) { + CurrentOffset += Read; + Data.resize(CurrentOffset + Read); + } + + if (Read == -1) { + Data.clear(); + close(FD); + return false; + } + + // Final resize to ensure there is no garbage data past the end. + Data.resize(CurrentOffset + Read); + + LoadedFile = true; } close(FD); - return Read == FileSize; + return LoadedFile; } ssize_t LoadFileToBuffer(const fextl::string &Filepath, std::span Buffer) { diff --git a/FEXCore/unittests/APITests/FileLoading.cpp b/FEXCore/unittests/APITests/FileLoading.cpp new file mode 100644 index 000000000..ebebfd144 --- /dev/null +++ b/FEXCore/unittests/APITests/FileLoading.cpp @@ -0,0 +1,22 @@ +#include +#include + +TEST_CASE("LoadFile-Doesn'tExist") { + fextl::string MapsFile; + auto Read = FEXCore::FileLoading::LoadFile(MapsFile, "/tmp/a/b/c/d/e/z"); + REQUIRE(MapsFile.size() == 0); + REQUIRE(Read == false); +} + +TEST_CASE("LoadFile-procfs") { + fextl::string MapsFile; + FEXCore::FileLoading::LoadFile(MapsFile, "/proc/self/maps"); + REQUIRE(MapsFile.size() != 0); +} + +TEST_CASE("LoadFile-Buffer") { + fextl::string MapsFile; + MapsFile.resize(16); + auto Read = FEXCore::FileLoading::LoadFileToBuffer("/proc/self/maps", MapsFile); + REQUIRE(MapsFile.size() == Read); +}