From 1d5174c4357c025a2db17fbee13a3b650fd9ef43 Mon Sep 17 00:00:00 2001 From: IgnatSergeev Date: Fri, 19 Jun 2026 04:18:05 +0300 Subject: [PATCH 1/6] Support TMP environment variables for tmp::file on POSIX --- src/create_file.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/create_file.cpp b/src/create_file.cpp index 4dde405..947263a 100644 --- a/src/create_file.cpp +++ b/src/create_file.cpp @@ -10,6 +10,10 @@ #include #include +#if __has_include() +#include +#endif + namespace tmp::detail { namespace fs = std::filesystem; @@ -18,11 +22,29 @@ namespace fs = std::filesystem; /// @returns A pointer to the file stream associated with the temporary file /// @throws fs::filesystem_error if cannot create a temporary file std::FILE* create_file() { +#if __has_include() + std::string path = fs::temp_directory_path() / "XXXXXX"; + int descriptor = mkstemp(path.data()); + if (descriptor == -1) { + std::error_code ec = std::error_code(errno, std::generic_category()); + throw fs::filesystem_error("Cannot create a temporary file", ec); + } + + unlink(path.data()); + + std::FILE* file = fdopen(descriptor, "wb+"); + if (file == nullptr) { + std::error_code ec = std::error_code(errno, std::generic_category()); + close(descriptor); + throw fs::filesystem_error("Cannot create a temporary file", ec); + } +#else std::FILE* file = std::tmpfile(); if (file == nullptr) { std::error_code ec = std::error_code(errno, std::generic_category()); throw fs::filesystem_error("Cannot create a temporary file", ec); } +#endif return file; } From 4a9078af61b86b2a6f4720a1cbd358b03467d9d0 Mon Sep 17 00:00:00 2001 From: bugdea1er Date: Tue, 14 Jul 2026 19:55:24 +0300 Subject: [PATCH 2/6] Add a test for temporary file location --- tests/directory.cpp | 1 - tests/file.cpp | 32 +++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/directory.cpp b/tests/directory.cpp index 16a605a..7182a31 100644 --- a/tests/directory.cpp +++ b/tests/directory.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include diff --git a/tests/file.cpp b/tests/file.cpp index e27abfa..8fada3e 100644 --- a/tests/file.cpp +++ b/tests/file.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -20,11 +19,14 @@ #include #else #include +#include #endif namespace tmp { namespace { +namespace fs = std::filesystem; + /// Test fixture for `basic_file` tests template class file : public testing::Test { public: @@ -85,6 +87,34 @@ TYPED_TEST(file, create) { basic_file tmpfile = basic_file(); EXPECT_TRUE(TestFixture::is_open(tmpfile.rdbuf())); EXPECT_TRUE(TestFixture::is_open(tmpfile.native_handle())); + + fs::path temp_dir = fs::temp_directory_path(); + auto handle = tmpfile.native_handle(); + +#ifdef _WIN32 + BY_HANDLE_FILE_INFORMATION file_info; + ASSERT_TRUE(GetFileInformationByHandle(handle, &file_info)); + + HANDLE dir_handle = + CreateFileW(temp_dir.c_str(), FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + ASSERT_NE(dir_handle, INVALID_HANDLE_VALUE); + + BY_HANDLE_FILE_INFORMATION dir_info; + ASSERT_TRUE(GetFileInformationByHandle(dir_handle, &dir_info)); + CloseHandle(dir_handle); + + EXPECT_EQ(file_info.dwVolumeSerialNumber, dir_info.dwVolumeSerialNumber); +#else + struct stat file_stat{}; + ASSERT_EQ(fstat(handle, &file_stat), 0); + + struct stat dir_stat{}; + ASSERT_EQ(stat(temp_dir.c_str(), &dir_stat), 0); + + EXPECT_EQ(file_stat.st_dev, dir_stat.st_dev); +#endif } /// Tests multiple file creation From 1666601d3bd5022370e2cbfd0b10cdee3832a84b Mon Sep 17 00:00:00 2001 From: bugdea1er Date: Tue, 14 Jul 2026 19:57:10 +0300 Subject: [PATCH 3/6] Add missing includes --- src/create_file.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/create_file.cpp b/src/create_file.cpp index 947263a..69144a3 100644 --- a/src/create_file.cpp +++ b/src/create_file.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #if __has_include() From 2646d96d9b7a6e6472b3fc45d457e938273ab681 Mon Sep 17 00:00:00 2001 From: bugdea1er Date: Tue, 14 Jul 2026 20:14:04 +0300 Subject: [PATCH 4/6] Fix weird clang-format issue --- tests/file.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/file.cpp b/tests/file.cpp index 8fada3e..9dbe8b3 100644 --- a/tests/file.cpp +++ b/tests/file.cpp @@ -107,10 +107,10 @@ TYPED_TEST(file, create) { EXPECT_EQ(file_info.dwVolumeSerialNumber, dir_info.dwVolumeSerialNumber); #else - struct stat file_stat{}; + struct stat file_stat {}; ASSERT_EQ(fstat(handle, &file_stat), 0); - struct stat dir_stat{}; + struct stat dir_stat {}; ASSERT_EQ(stat(temp_dir.c_str(), &dir_stat), 0); EXPECT_EQ(file_stat.st_dev, dir_stat.st_dev); From 165499214f69132981a6a9365320451e1527ff82 Mon Sep 17 00:00:00 2001 From: bugdea1er Date: Tue, 14 Jul 2026 20:21:07 +0300 Subject: [PATCH 5/6] Use `system_category` for POSIX errno --- src/create_file.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/create_file.cpp b/src/create_file.cpp index 69144a3..3d52c84 100644 --- a/src/create_file.cpp +++ b/src/create_file.cpp @@ -27,7 +27,7 @@ std::FILE* create_file() { std::string path = fs::temp_directory_path() / "XXXXXX"; int descriptor = mkstemp(path.data()); if (descriptor == -1) { - std::error_code ec = std::error_code(errno, std::generic_category()); + std::error_code ec = std::error_code(errno, std::system_category()); throw fs::filesystem_error("Cannot create a temporary file", ec); } @@ -35,7 +35,7 @@ std::FILE* create_file() { std::FILE* file = fdopen(descriptor, "wb+"); if (file == nullptr) { - std::error_code ec = std::error_code(errno, std::generic_category()); + std::error_code ec = std::error_code(errno, std::system_category()); close(descriptor); throw fs::filesystem_error("Cannot create a temporary file", ec); } From 13ae3d95b9c31e276cf736aaa033618052515c19 Mon Sep 17 00:00:00 2001 From: bugdea1er Date: Tue, 14 Jul 2026 20:32:02 +0300 Subject: [PATCH 6/6] Use `O_TMPFILE` on Linux --- src/create_file.cpp | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/create_file.cpp b/src/create_file.cpp index 3d52c84..88a0be9 100644 --- a/src/create_file.cpp +++ b/src/create_file.cpp @@ -12,6 +12,8 @@ #include #if __has_include() +#include +#include #include #endif @@ -19,13 +21,26 @@ namespace tmp::detail { namespace fs = std::filesystem; -/// Creates and opens a binary temporary file as if by POSIX `tmpfile` -/// @returns A pointer to the file stream associated with the temporary file -/// @throws fs::filesystem_error if cannot create a temporary file -std::FILE* create_file() { +namespace { + #if __has_include() - std::string path = fs::temp_directory_path() / "XXXXXX"; - int descriptor = mkstemp(path.data()); +/// Creates and opens a binary temporary file as if by POSIX `mkstemp` +/// @returns A file descriptor associated with the temporary file +/// @throws fs::filesystem_error if cannot create a temporary file +int create_file_descriptor() { + fs::path temp_dir = fs::temp_directory_path(); + int descriptor = int{}; + +#ifdef __linux__ + descriptor = open(temp_dir.c_str(), O_RDWR | O_TMPFILE, S_IRUSR | S_IWUSR); + if (descriptor >= 0) { + return descriptor; + } +#endif + + std::string path = temp_dir / "XXXXXX"; + + descriptor = mkstemp(path.data()); if (descriptor == -1) { std::error_code ec = std::error_code(errno, std::system_category()); throw fs::filesystem_error("Cannot create a temporary file", ec); @@ -33,10 +48,24 @@ std::FILE* create_file() { unlink(path.data()); + return descriptor; +} +#endif +} // namespace + +/// Creates and opens a binary temporary file as if by POSIX `tmpfile` +/// @returns A pointer to the file stream associated with the temporary file +/// @throws fs::filesystem_error if cannot create a temporary file +std::FILE* create_file() { +#if __has_include() + int descriptor = create_file_descriptor(); + + // TODO: let `filebuf` use the file descriptor without `FILE` wrapping std::FILE* file = fdopen(descriptor, "wb+"); if (file == nullptr) { std::error_code ec = std::error_code(errno, std::system_category()); close(descriptor); + throw fs::filesystem_error("Cannot create a temporary file", ec); } #else