Fix race condition when creating unique filename

Because the file is not opened with O_EXCL, there is no guarantee that
multiple threads calling this function simultaneously across different
processes will not generate the same filename. Instead make sure to use
O_EXCL for the kernel to decide whether a filename already exists.
This commit is contained in:
Ludovic Henry 2024-02-26 16:14:48 +00:00 committed by Ludovic Henry
parent 9d43b27f7a
commit 73245c3baf
No known key found for this signature in database
2 changed files with 30 additions and 3 deletions

View File

@ -302,6 +302,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <fcntl.h>
#include <cerrno> #include <cerrno>
// #include <condition_variable> // Guarded by GTEST_IS_THREADSAFE below // #include <condition_variable> // Guarded by GTEST_IS_THREADSAFE below
@ -2112,6 +2113,24 @@ GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
!defined(GTEST_OS_XTENSA) && !defined(GTEST_OS_QURT) !defined(GTEST_OS_XTENSA) && !defined(GTEST_OS_QURT)
inline int ChDir(const char* dir) { return chdir(dir); } inline int ChDir(const char* dir) { return chdir(dir); }
#endif #endif
#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW)
const int CREAT = _O_CREAT;
const int EXCL = _O_EXCL;
const int WRONLY = _O_WRONLY;
#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
const int CREAT = O_CREAT;
const int EXCL = O_EXCL;
const int WRONLY = O_WRONLY;
#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
inline int Open(const char* path, int flags, int mode) {
#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW)
std::wstring_convert<wchar_codecvt> converter;
std::wstring wide_path = converter.from_bytes(path);
return _wopen(wide_path.c_str(), flags, mode);
#else // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
return open(path, flags, mode);
#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
}
inline FILE* FOpen(const char* path, const char* mode) { inline FILE* FOpen(const char* path, const char* mode) {
#if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW) #if defined(GTEST_OS_WINDOWS) && !defined(GTEST_OS_WINDOWS_MINGW)
struct wchar_codecvt : public std::codecvt<wchar_t, char, std::mbstate_t> {}; struct wchar_codecvt : public std::codecvt<wchar_t, char, std::mbstate_t> {};

View File

@ -312,12 +312,20 @@ bool FilePath::IsAbsolutePath() const { return CalculateRootLength() > 0; }
FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
const FilePath& base_name, const FilePath& base_name,
const char* extension) { const char* extension) {
// Make sure the directory does exist
directory.CreateDirectoriesRecursively();
FilePath full_pathname; FilePath full_pathname;
int number = 0; int number = 0;
do { for (;;) {
full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
} while (full_pathname.FileOrDirectoryExists()); int fd = posix::Open(full_pathname.c_str(),
return full_pathname; posix::CREAT | posix::EXCL | posix::WRONLY, 0644);
if (fd != -1) {
posix::Close(fd);
return full_pathname;
}
}
} }
// Returns true if FilePath ends with a path separator, which indicates that // Returns true if FilePath ends with a path separator, which indicates that