INISettingsInterface: Fix file leak on Linux

This commit is contained in:
Stenzek 2024-04-25 02:03:13 +10:00
parent 9d3f328404
commit 425235fd31
No known key found for this signature in database

View file

@ -19,31 +19,49 @@ Log_SetChannel(INISettingsInterface);
#include <io.h> // _mktemp_s #include <io.h> // _mktemp_s
#else #else
#include <stdlib.h> // mktemp #include <stdlib.h> // mktemp
#include <unistd.h>
#endif #endif
// To prevent races between saving and loading settings, particularly with game settings, // To prevent races between saving and loading settings, particularly with game settings,
// we only allow one ini to be parsed at any point in time. // we only allow one ini to be parsed at any point in time.
static std::mutex s_ini_load_save_mutex; static std::mutex s_ini_load_save_mutex;
static std::string GetTemporaryFileName(const std::string& original_filename) static std::FILE* GetTemporaryFile(std::string* temporary_filename, const std::string& original_filename,
const char* mode, Error* error)
{ {
std::string temporary_filename; temporary_filename->clear();
temporary_filename.reserve(original_filename.length() + 8); temporary_filename->reserve(original_filename.length() + 8);
temporary_filename.append(original_filename); temporary_filename->append(original_filename);
#ifdef _WIN32 #ifdef _WIN32
temporary_filename.append(".XXXXXXX"); temporary_filename->append(".XXXXXXX");
_mktemp_s(temporary_filename.data(), temporary_filename.length() + 1); const errno_t err = _mktemp_s(temporary_filename->data(), temporary_filename->length() + 1);
#else if (err != 0)
temporary_filename.append(".XXXXXX"); {
#if defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__) Error::SetErrno(error, "_mktemp_s() failed: ", err);
mkstemp(temporary_filename.data()); return nullptr;
#else }
mktemp(temporary_filename.data());
#endif
#endif
return temporary_filename; return FileSystem::OpenCFile(temporary_filename->c_str(), mode, error);
#else
temporary_filename->append(".XXXXXX");
const int fd = mkstemp(temporary_filename->data());
if (fd < 0)
{
Error::SetErrno(error, "mkstemp() failed: ", errno);
return nullptr;
}
std::FILE* fp = fdopen(fd, mode);
if (!fp)
{
Error::SetErrno(error, "mkstemp() failed: ", errno);
close(fd);
return nullptr;
}
return fp;
#endif
} }
INISettingsInterface::INISettingsInterface(std::string filename) : m_filename(std::move(filename)), m_ini(true, true) INISettingsInterface::INISettingsInterface(std::string filename) : m_filename(std::move(filename)), m_ini(true, true)
@ -79,9 +97,9 @@ bool INISettingsInterface::Save(Error* error /* = nullptr */)
} }
std::unique_lock lock(s_ini_load_save_mutex); std::unique_lock lock(s_ini_load_save_mutex);
std::string temp_filename(GetTemporaryFileName(m_filename)); std::string temp_filename;
std::FILE* fp = GetTemporaryFile(&temp_filename, m_filename, "wb", error);
SI_Error err = SI_FAIL; SI_Error err = SI_FAIL;
std::FILE* fp = FileSystem::OpenCFile(temp_filename.c_str(), "wb", error);
if (fp) if (fp)
{ {
err = m_ini.SaveFile(fp, false); err = m_ini.SaveFile(fp, false);